//Refactored version of WTL Ribbon
//by Arturas Matusak
//Do no not modify this header comment!
// Windows Template Library - WTL version 8.1
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// This file is a part of the Windows Template Library.
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license. You must not remove this notice, or
// any other, from this software.

#ifndef __ATLRIBBON_H__
#define __ATLRIBBON_H__

#pragma once

#if (_MSC_VER < 1500)
	#error atlribbon.h requires Visual C++ 2008 compiler or higher
#endif

#ifndef _UNICODE
	#error atlribbon.h requires the Unicode character set
#endif

#if !defined(NTDDI_WIN7) || (NTDDI_VERSION < NTDDI_WIN7)
	#error atlribbon.h requires the Windows 7 SDK or higher
#endif

#ifdef _WIN32_WCE
	#error atlribbon.h is not supported on Windows CE
#endif

#ifndef __ATLAPP_H__
	#error atlribbon.h requires atlapp.h to be included first
#endif

#if (_ATL_VER < 0x0700)
	#include <shlwapi.h>
	#pragma comment(lib, "shlwapi.lib")
#endif

#ifndef __ATLCOLL_H__
	#error atlribbon.h requires atlcoll.h to be included first
//TODO: remove this or add STL::vector
#endif

#include <atlmisc.h>    // for RecentDocumentList classes
#include <atlframe.h>   // for Frame and UpdateUI classes
#include <atlctrls.h>   // required for atlctrlw.h
#include <atlctrlw.h>   // for CCommandBarCtrl

#if !defined(_WTL_USE_CSTRING) && !defined(__ATLSTR_H__)
  #pragma warning(disable : 4530)   // unwind semantics not enabled
  #include <string>
  #pragma warning(default : 4530)
#endif

#include <dwmapi.h>
#pragma comment(lib, "dwmapi.lib")

#include <UIRibbon.h>
#include <UIRibbonPropertyHelpers.h>
#pragma comment(lib, "propsys.lib")

#include <Richedit.h>   // for CHARFORMAT2


///////////////////////////////////////////////////////////////////////////////
// Classes in this file:
//
// CRibbonUpdateUI : Automatic mapping of ribbon UI elements
//
// RibbonUI::Text
// RibbonUI::CharFormat
// RibbonUI::ICtrl
// RibbonUI::CBaseCtrlImpl
// RibbonUI::CButtonCtrl
// RibbonUI::CCallbackProperty
// RibbonUI::CCallbackCollection
// RibbonUI::CCategoryCollection
// RibbonUI::CTextCollection
// RibbonUI::CImageCollection
// RibbonUI::CommandCollectionImpl
// RibbonUI::ToolbarCollectionImpl
// RibbonUI::SimpleCollectionImpl
// RibbonUI::CollectionCtrlImpl
// RibbonUI::CToolbarGalleryCtrlImpl
// RibbonUI::SimpleCollectionCtrlImpl
// RibbonUI::RecentItemsCtrlImpl
//
// RibbonUI::CRibbonImpl
//	 CRibbonImpl::CRibbonComboCtrl
//	 CRibbonImpl::CRibbonItemGalleryCtrl
//	 CRibbonImpl::CRibbonCommandGalleryCtrl
//	 CRibbonImpl::CRibbonToolbarGalleryCtrl
//	 CRibbonImpl::CRibbonSimpleComboCtrl
//	 CRibbonImpl::CRibbonSimpleGalleryCtrl
//	 CRibbonImpl::CRibbonRecentItemsCtrl
//	 CRibbonImpl::CRibbonColorCtrl
//	 CRibbonImpl::CRibbonFontCtrl
//	 CRibbonImpl::CRibbonSpinnerCtrl
//	 CRibbonImpl::CRibbonFloatSpinnerCtrl
//	 CRibbonImpl::CRibbonCommandCtrl
//
// CRibbonFrameWindowImplBase
// CRibbonFrameWindowImpl
// CRibbonMDIFrameWindowImpl
// CRibbonPersist
//
// Global functions:
//   RibbonUI::SetPropertyVal()
//   RibbonUI::GetImageCopy()


// Constants

#ifndef RIBBONUI_MAX_TEXT
  #define RIBBONUI_MAX_TEXT 128
#endif

#define TWIPS_PER_POINT 20   // For font size


namespace WTL
{

///////////////////////////////////////////////////////////////////////////////
// CRibbonUpdateUI : Automatic mapping of ribbon UI elements

template <class TFrame>
class CRibbonUpdateUI : public CAutoUpdateUI<TFrame>
{
public:
	enum
	{
		UPDUI_RIBBON = 0x0080, 
		UPDUI_PERSIST = 0x0020
	};

	bool IsRibbonElement(const _AtlUpdateUIMap& UIMap)
	{
		return (UIMap.m_wType & UPDUI_RIBBON) != 0;
	}

	bool IsRibbonID(UINT nID)
	{
		for(int i = 0; i < m_arrUIMap.GetSize(); i++)
		{
			if(m_arrUIMap[i].m_nID == nID)
				return IsRibbonElement(m_arrUIMap[i]);
		}

		return false;
	}

// Element
	bool UIAddRibbonElement(UINT nID)
	{
		return UIAddElement<UPDUI_RIBBON>(nID);
	}

	bool UIRemoveRibbonElement(UINT nID)
	{
		return UIRemoveElement<UPDUI_RIBBON>(nID);
	}

	bool UIPersistElement(UINT nID, bool bPersist = true)
	{
		return bPersist ?
			UIAddElement<UPDUI_PERSIST>(nID) :
			UIRemoveElement<UPDUI_PERSIST>(nID);
	}

// methods for Ribbon elements
	BOOL UISetText(int nID, LPCWSTR sText, BOOL bForceUpdate = FALSE)
	{
		TFrame* pFrame = static_cast<TFrame*>(this);
		BOOL bRes = CUpdateUIBase::UISetText(nID, sText, bForceUpdate);
		if (pFrame->IsRibbonUI() && IsRibbonID(nID))
			bRes = SUCCEEDED(pFrame->InvalidateProperty(nID, UI_PKEY_Label));
		return bRes;
	}

	BOOL UISetText(int nID, UINT uIdResource, BOOL bForceUpdate = FALSE)
	{
		CTempBuffer<WCHAR> sText(RIBBONUI_MAX_TEXT);
		return AtlLoadString(uIdResource, sText, RIBBONUI_MAX_TEXT) ? 
			UISetText(nID, sText, bForceUpdate) :
			E_FAIL;
	}

	LPCTSTR UIGetText(int nID)
	{
		TFrame* pFrame = static_cast<TFrame*>(this);
		LPCTSTR sUI = CAutoUpdateUI::UIGetText(nID);
		
		// replace 'tab' by 'space' for RibbonUI elements
		if (sUI && pFrame->IsRibbonUI() && IsRibbonID(nID) && wcschr(sUI, L'\t'))
		{
			static WCHAR sText[RIBBONUI_MAX_TEXT] = { 0 };
			wcscpy_s(sText, sUI);
			*wcschr(sText, L'\t') = L' ';
			return sText;
		}
		else
		{
			return sUI;
		}
	}

	BOOL UIEnable(int nID, BOOL bEnable, BOOL bForceUpdate = FALSE)
	{
		TFrame* pFrame = static_cast<TFrame*>(this);
		BOOL bRes = CUpdateUIBase::UIEnable(nID, bEnable, bForceUpdate);
		if (pFrame->IsRibbonUI() && IsRibbonID(nID))
			bRes = SUCCEEDED(pFrame->SetProperty((WORD)nID, UI_PKEY_Enabled, bEnable));
		return bRes;
	}

	BOOL UISetCheck(int nID, INT nCheck, BOOL bForceUpdate = FALSE)
	{
		if ((nCheck == 0) || (nCheck == 1))
			return UISetCheck(nID, nCheck != 0, bForceUpdate);
		else
			return CUpdateUIBase::UISetCheck(nID, nCheck, bForceUpdate);
	}

	BOOL UISetCheck(int nID, bool bCheck, BOOL bForceUpdate = FALSE)
	{
		TFrame* pFrame = static_cast<TFrame*>(this);
		BOOL bRes = CUpdateUIBase::UISetCheck(nID, bCheck, bForceUpdate);
		if (bRes && pFrame->IsRibbonUI() && IsRibbonID(nID))
			bRes = SUCCEEDED(pFrame->SetProperty((WORD)nID, UI_PKEY_BooleanValue, bCheck));
		return bRes;
	}
};


///////////////////////////////////////////////////////////////////////////////
// RibbonUI namespace
//

namespace RibbonUI
{

// Minimal string allocation support for various PROPERTYKEY values
#if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
  typedef _CSTRING_NS::CString Text;
#else
  class Text : public std::wstring
  {
  public:
	Text(std::wstring& s) : std::wstring(s)
	{ }
	Text(LPCWSTR s) : std::wstring(s)
	{ }
	Text()
	{ }
	bool IsEmpty()
	{
		return empty();
	}
	operator LPCWSTR()
	{
		return c_str();
	}
	Text& operator =(LPCWSTR s)
	{
		return static_cast<Text&>(std::wstring::operator =(s));
	}
  };
#endif

// PROPERTYKEY enum and helpers
enum k_KEY
{
	// state
	k_Enabled = 1, k_BooleanValue = 200, 
	// text properties
	k_LabelDescription = 2, k_Keytip = 3, k_Label = 4, k_TooltipDescription = 5, k_TooltipTitle = 6, 
	// image properties
	k_LargeImage = 7, k_LargeHighContrastImage = 8, k_SmallImage = 9, k_SmallHighContrastImage = 10,
	// collection properties
	k_ItemsSource = 101, k_Categories = 102, k_SelectedItem = 104,
	// collection item properties
	k_CommandId = 100, k_CategoryId = 103, k_CommandType = 105, k_ItemImage = 106,
	// combo control property
	k_StringValue = 202,
	// spinner control properties
	k_DecimalValue = 201, k_MaxValue = 203, k_MinValue, k_Increment, k_DecimalPlaces, k_FormatString, k_RepresentativeString = 208,
	// font control properties
	k_FontProperties = 300, k_FontProperties_Family, k_FontProperties_Size, k_FontProperties_Bold, k_FontProperties_Italic = 304, 
	k_FontProperties_Underline = 305, k_FontProperties_Strikethrough, k_FontProperties_VerticalPositioning, k_FontProperties_ForegroundColor = 308, 
	k_FontProperties_BackgroundColor = 309, k_FontProperties_ForegroundColorType, k_FontProperties_BackgroundColorType, k_FontProperties_ChangedProperties = 312, 
	k_FontProperties_DeltaSize = 313, 
	// recent items properties
	k_RecentItems = 350, k_Pinned = 351,
	// colour control properties
	k_Color = 400, k_ColorType = 401, k_ColorMode, 
	k_ThemeColorsCategoryLabel = 403, k_StandardColorsCategoryLabel, k_RecentColorsCategoryLabel = 405, k_AutomaticColorLabel = 406, 
	k_NoColorLabel = 407, k_MoreColorsLabel = 408, 
	k_ThemeColors = 409, k_StandardColors = 410, k_ThemeColorsTooltips = 411, k_StandardColorsTooltips = 412,
	// Ribbon state
	k_Viewable = 1000, k_Minimized = 1001, k_QuickAccessToolbarDock = 1002, k_ContextAvailable = 1100,
	// Ribbon UI colours
	k_GlobalBackgroundColor = 2000, k_GlobalHighlightColor, k_GlobalTextColor = 2002
};

inline k_KEY k_(REFPROPERTYKEY key)
{
	return (k_KEY)key.fmtid.Data1;
}

// PROPERTYKEY value assignment and specializations
//
template <typename V>
HRESULT SetPropertyVal(REFPROPERTYKEY key, V val, PROPVARIANT* ppv)
{
	switch (k_(key))
	{
	case k_Enabled:
	case k_BooleanValue:
		return InitPropVariantFromBoolean(val, ppv);
	default:
		return UIInitPropertyFromUInt32(key, val, ppv);
	}
}

inline HRESULT SetPropertyVal(REFPROPERTYKEY key, DOUBLE val, PROPVARIANT* ppv)
{
	return SetPropertyVal(key, (LONG)val, ppv);
}

inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IUIImage* val, PROPVARIANT* ppv)
{
	HRESULT hr = UIInitPropertyFromImage(key, val, ppv);
	ATLVERIFY(val->Release() == 1);
	return hr;
}

inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IUnknown* val, PROPVARIANT* ppv)
{
	return UIInitPropertyFromInterface(key, val, ppv);
}

inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IPropertyStore* val, PROPVARIANT* ppv)
{
	return UIInitPropertyFromInterface(key, val, ppv);
}

inline HRESULT SetPropertyVal(REFPROPERTYKEY key, SAFEARRAY* val, PROPVARIANT* ppv)
{
	return UIInitPropertyFromIUnknownArray(key, val, ppv);
}

inline HRESULT SetPropertyVal(REFPROPERTYKEY key, DECIMAL* val, PROPVARIANT* ppv)
{
	return UIInitPropertyFromDecimal(key, *val, ppv);
}

inline HRESULT SetPropertyVal(REFPROPERTYKEY key, bool val, PROPVARIANT* ppv)
{
	return UIInitPropertyFromBoolean(key, val, ppv);
}

inline HRESULT SetPropertyVal(REFPROPERTYKEY key, LPCWSTR val, PROPVARIANT* ppv)
{
	return UIInitPropertyFromString(key, val, ppv);
}

// CharFormat helper struct for RibbonUI font control
//
struct CharFormat : CHARFORMAT2
{
	// Default constructor
	CharFormat()
	{
		cbSize = sizeof CHARFORMAT2;
		Reset();
	}

	// Copy constructor
	CharFormat(const CharFormat& cf)
	{
		CopyMemory(this, &cf, sizeof CHARFORMAT2);
	}

	// Assign operator
	CharFormat& operator =(const CharFormat& cf)
	{
		CopyMemory(this, &cf, sizeof CHARFORMAT2);
		return (*this);
	}

	void Reset()
	{
		uValue = dwMask = dwEffects = 0;
		PropVariantInit(&propvar);
	}

	void operator <<(IPropertyStore* pStore)
	{
		if (pStore == NULL)
		{
			ATLASSERT(FALSE);
			return;
		}

		static void (CharFormat::*Getk_[])(IPropertyStore*) = 
		{
			&CharFormat::Getk_Family, 
			&CharFormat::Getk_FontProperties_Size, 
			&CharFormat::Getk_MaskEffect<CFM_BOLD, CFE_BOLD, UI_PKEY_FontProperties_Bold>,
			&CharFormat::Getk_MaskEffect<CFM_ITALIC, CFE_ITALIC, UI_PKEY_FontProperties_Italic>,
			&CharFormat::Getk_MaskEffect<CFM_UNDERLINE, CFE_UNDERLINE, UI_PKEY_FontProperties_Underline>,
			&CharFormat::Getk_MaskEffect<CFM_STRIKEOUT, CFE_STRIKEOUT, UI_PKEY_FontProperties_Strikethrough>,
			&CharFormat::Getk_VerticalPositioning, 
			&CharFormat::Getk_Color<CFM_COLOR, UI_PKEY_FontProperties_ForegroundColor>, 
			&CharFormat::Getk_Color<CFM_BACKCOLOR, UI_PKEY_FontProperties_BackgroundColor>, 
			&CharFormat::Getk_ColorType<CFM_COLOR, CFE_AUTOCOLOR, UI_SWATCHCOLORTYPE_AUTOMATIC, UI_PKEY_FontProperties_ForegroundColorType>,
			&CharFormat::Getk_ColorType<CFM_BACKCOLOR, CFE_AUTOBACKCOLOR, UI_SWATCHCOLORTYPE_NOCOLOR, UI_PKEY_FontProperties_BackgroundColorType>,
		};

		DWORD nProps = 0;
		Reset();

		ATLVERIFY(SUCCEEDED(pStore->GetCount(&nProps)));
		for (DWORD iProp = 0; iProp < nProps; iProp++)
		{
			PROPERTYKEY key;	
			ATLVERIFY(SUCCEEDED(pStore->GetAt(iProp, &key)));
			ATLASSERT(k_(key) >= k_FontProperties_Family);

			if (k_(key) <= k_FontProperties_BackgroundColorType)
				(this->*Getk_[k_(key) - k_FontProperties_Family])(pStore);
		}
	}

	void operator >>(IPropertyStore* pStore)
	{
		if (pStore == NULL)
		{
			ATLASSERT(FALSE);
			return;
		}

		PutFace(pStore);
		PutSize(pStore);
		PutMaskEffect(CFM_BOLD, CFE_BOLD, UI_PKEY_FontProperties_Bold, pStore);
		PutMaskEffect(CFM_ITALIC, CFE_ITALIC, UI_PKEY_FontProperties_Italic, pStore);
		PutMaskEffect(CFM_UNDERLINE, CFE_UNDERLINE, UI_PKEY_FontProperties_Underline, pStore);
		PutMaskEffect(CFM_STRIKEOUT, CFE_STRIKEOUT, UI_PKEY_FontProperties_Strikethrough, pStore);
		PutVerticalPos(pStore);
		PutColor(pStore);
		PutBackColor(pStore);
	}

private:
	PROPVARIANT propvar;
	UINT uValue;

	// Getk_ functions
	void Getk_Family(IPropertyStore* pStore)
	{
		if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_Family, &propvar)))
		{
			PropVariantToString(propvar, szFaceName, LF_FACESIZE);
			if (*szFaceName)
				dwMask |= CFM_FACE;
		}
	}

	void Getk_FontProperties_Size(IPropertyStore* pStore)
	{
		if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_Size, &propvar)))
		{
			DECIMAL decSize;
			UIPropertyToDecimal(UI_PKEY_FontProperties_Size, propvar, &decSize);
			DOUBLE dSize;
			VarR8FromDec(&decSize, &dSize);
			if (dSize > 0)
			{
				dwMask |= CFM_SIZE;
				yHeight = (LONG)(dSize * TWIPS_PER_POINT);
			}
		}
	}

	template <DWORD t_dwMask, DWORD t_dwEffects, REFPROPERTYKEY key>
	void Getk_MaskEffect(IPropertyStore* pStore)
	{
		if (SUCCEEDED(pStore->GetValue(key, &propvar)))
		{
			UIPropertyToUInt32(key, propvar, &uValue);
			if ((UI_FONTPROPERTIES)uValue != UI_FONTPROPERTIES_NOTAVAILABLE)
			{
				dwMask |= t_dwMask;
				dwEffects |= ((UI_FONTPROPERTIES) uValue == UI_FONTPROPERTIES_SET) ? t_dwEffects : 0;
			}
		}	
	}

	void Getk_VerticalPositioning(IPropertyStore* pStore)
	{
		if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_VerticalPositioning, &propvar)))
		{
			UIPropertyToUInt32(UI_PKEY_FontProperties_VerticalPositioning, propvar, &uValue);
			UI_FONTVERTICALPOSITION uVerticalPosition = (UI_FONTVERTICALPOSITION) uValue;
			if ((uVerticalPosition != UI_FONTVERTICALPOSITION_NOTAVAILABLE))
			{
				dwMask |= (CFM_SUPERSCRIPT | CFM_SUBSCRIPT);
				if (uVerticalPosition != UI_FONTVERTICALPOSITION_NOTSET)
				{
					dwEffects |= (uVerticalPosition == UI_FONTVERTICALPOSITION_SUPERSCRIPT) ? CFE_SUPERSCRIPT : CFE_SUBSCRIPT;
				}
			}
		}
	}

	template <DWORD t_dwMask, REFPROPERTYKEY key>
	void Getk_Color(IPropertyStore* pStore)
	{
		UINT32 color;
		if (SUCCEEDED(pStore->GetValue(key, &propvar)))
		{
			UIPropertyToUInt32(key, propvar, &color);
			dwMask |= t_dwMask;

			if (t_dwMask == CFM_COLOR)
				crTextColor = color;
			else
				crBackColor = color;
		}
	}

	template <DWORD t_dwMask, DWORD t_dwEffects, UI_SWATCHCOLORTYPE t_type, REFPROPERTYKEY key>
	void Getk_ColorType(IPropertyStore* pStore)
	{
		if (SUCCEEDED(pStore->GetValue(key, &propvar)))
		{
			UIPropertyToUInt32(key, propvar, &uValue);
			if (t_type == (UI_SWATCHCOLORTYPE)uValue)
			{
				dwMask |= t_dwMask;
				dwEffects |= t_dwEffects;
			}
		}
	}

	// Put functions
	void PutMaskEffect(WORD dwMaskVal, WORD dwEffectVal, REFPROPERTYKEY key, IPropertyStore* pStore)
	{
		PROPVARIANT propvar;
		UI_FONTPROPERTIES uProp = UI_FONTPROPERTIES_NOTAVAILABLE;
		if ((dwMask & dwMaskVal) != 0)
			uProp = dwEffects & dwEffectVal ? UI_FONTPROPERTIES_SET : UI_FONTPROPERTIES_NOTSET;
		SetPropertyVal(key, uProp, &propvar);
		pStore->SetValue(key, propvar);
	}

	void PutVerticalPos(IPropertyStore* pStore)
	{
		PROPVARIANT propvar;
		UI_FONTVERTICALPOSITION uProp = UI_FONTVERTICALPOSITION_NOTAVAILABLE;

		if ((dwMask & CFE_SUBSCRIPT) != 0)
		{
			if ((dwMask & CFM_SUBSCRIPT) && (dwEffects & CFE_SUBSCRIPT))
				uProp = UI_FONTVERTICALPOSITION_SUBSCRIPT;
			else
				uProp = UI_FONTVERTICALPOSITION_SUPERSCRIPT;
		}
		else if ((dwMask & CFM_OFFSET) != 0)
		{
			if (yOffset > 0)
				uProp = UI_FONTVERTICALPOSITION_SUPERSCRIPT;
			else if (yOffset < 0)
				uProp = UI_FONTVERTICALPOSITION_SUBSCRIPT;
		}

		SetPropertyVal(UI_PKEY_FontProperties_VerticalPositioning, uProp, &propvar);
		pStore->SetValue(UI_PKEY_FontProperties_VerticalPositioning, propvar);
	}

	void PutFace(IPropertyStore* pStore)
	{
		PROPVARIANT propvar;
		SetPropertyVal(UI_PKEY_FontProperties_Family, 
			dwMask & CFM_FACE ? szFaceName : L"", &propvar);
		pStore->SetValue(UI_PKEY_FontProperties_Family, propvar);
	}

	void PutSize(IPropertyStore* pStore)
	{
		PROPVARIANT propvar;
		DECIMAL decVal;

		if ((dwMask & CFM_SIZE) != 0)
			VarDecFromR8((DOUBLE)yHeight / TWIPS_PER_POINT, &decVal);
		else
			VarDecFromI4(0, &decVal);

		SetPropertyVal(UI_PKEY_FontProperties_Size, &decVal, &propvar);
		pStore->SetValue(UI_PKEY_FontProperties_Size, propvar);
	}

	void PutColor(IPropertyStore* pStore)
	{
		if ((dwMask & CFM_COLOR) != 0)
			if ((dwEffects & CFE_AUTOCOLOR) == 0)
			{
				SetPropertyVal(UI_PKEY_FontProperties_ForegroundColorType, UI_SWATCHCOLORTYPE_RGB, &propvar);
				pStore->SetValue(UI_PKEY_FontProperties_ForegroundColorType, propvar);
				
				SetPropertyVal(UI_PKEY_FontProperties_ForegroundColor, crTextColor, &propvar);
				pStore->SetValue(UI_PKEY_FontProperties_ForegroundColor, propvar);
			}
			else
			{
				SetPropertyVal(UI_PKEY_FontProperties_ForegroundColorType, UI_SWATCHCOLORTYPE_AUTOMATIC, &propvar);
				pStore->SetValue(UI_PKEY_FontProperties_ForegroundColorType, propvar);
			}
	}

	void PutBackColor(IPropertyStore* pStore)
	{
		if (((dwMask & CFM_BACKCOLOR) != 0) && ((dwEffects & CFE_AUTOBACKCOLOR) == 0))
		{
			SetPropertyVal(UI_PKEY_FontProperties_BackgroundColorType, UI_SWATCHCOLORTYPE_RGB, &propvar);
			pStore->SetValue(UI_PKEY_FontProperties_BackgroundColorType, propvar);
				
			SetPropertyVal(UI_PKEY_FontProperties_BackgroundColor, crBackColor, &propvar);
			pStore->SetValue(UI_PKEY_FontProperties_BackgroundColor, propvar);
		}
		else
		{
			SetPropertyVal(UI_PKEY_FontProperties_BackgroundColorType, UI_SWATCHCOLORTYPE_NOCOLOR, &propvar);
			pStore->SetValue(UI_PKEY_FontProperties_BackgroundColorType, propvar);
		}
	}
};

// IUIImage helper
//
inline IUIImage* GetImageCopy(HBITMAP hbm, UI_OWNERSHIP owner)
{
	ATLASSERT(hbm);
	IUIImage* pIUII = NULL;
	ATL::CComPtr<IUIImageFromBitmap> pIFB;

	if SUCCEEDED(pIFB.CoCreateInstance(CLSID_UIRibbonImageFromBitmapFactory))
		ATLVERIFY(SUCCEEDED(pIFB->CreateImage(hbm, owner, &pIUII)));

	return pIUII;
}


///////////////////////////////////////////////////////////////////////////////
// Ribbon control classes

// RibbonUI::ICtrl abstract interface of RibbonUI::CRibbonImpl and all RibbonUI control classes
//
struct ICtrl
{
	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* pCommandExecutionProperties) = 0;

	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) = 0;
};

// RibbonUI::CBaseCtrlImpl base class for all ribbon controls
//
template <class TWindow>
class ATL_NO_VTABLE CBaseCtrlImpl : public ICtrl
{
protected:
	TWindow* m_pWndRibbon;
	WORD m_wID;
public:
	typedef TWindow WndRibbon;

	CBaseCtrlImpl(WORD wID) : 
		m_pWndRibbon(TWindow::pWndRibbon),
		m_wID(wID)
	{ }

	WndRibbon& GetWndRibbon()
	{
		return *m_pWndRibbon;
	}

	WORD GetID()
	{
		return m_wID;
	}

	Text m_sTxt[5];

	// Operations
	HRESULT Invalidate()
	{
		return GetWndRibbon().InvalidateCtrl(GetID());
	}

	HRESULT Invalidate(REFPROPERTYKEY key, UI_INVALIDATIONS flags = UI_INVALIDATIONS_PROPERTY)
	{
		return GetWndRibbon().InvalidateProperty(GetID(), key, flags);
	}

	HRESULT SetText(REFPROPERTYKEY key, LPCWSTR sTxt, bool bUpdate = false)
	{
		ATLASSERT((k_(key) <= k_TooltipTitle) && (k_(key) >= k_LabelDescription));

		m_sTxt[k_(key) - k_LabelDescription] = sTxt;

		return bUpdate ?
			GetWndRibbon().InvalidateProperty(GetID(), key) :
			S_OK;
	}

	// Implementation
	template <typename V>
	HRESULT SetProperty(REFPROPERTYKEY key, V val)
	{
		return GetWndRibbon().SetProperty(GetID(), key, val);
	}

	HRESULT OnGetText(REFPROPERTYKEY key, PROPVARIANT* ppv)
	{
		ATLASSERT((k_(key) <= k_TooltipTitle) && (k_(key) >= k_LabelDescription));

		const INT iText = k_(key) - k_LabelDescription;
		if (m_sTxt[iText].IsEmpty())
			if (LPCWSTR sText = GetWndRibbon().OnRibbonQueryText(GetID(), key))
				m_sTxt[iText] = sText;

		return !m_sTxt[iText].IsEmpty() ?
			SetPropertyVal(key, (LPCWSTR)m_sTxt[iText], ppv) :
			S_OK;
	}

	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* pCommandExecutionProperties)
	{
		ATLASSERT(nCmdID == GetID());
		return GetWndRibbon().DoExecute(nCmdID, verb, key, ppropvarValue, pCommandExecutionProperties);
	}

	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT(nCmdID == GetID());

		const INT iMax = k_TooltipTitle - k_LabelDescription;
		const INT iVal = k_(key) - k_LabelDescription;

		return (iVal <= iMax) && (iVal >= 0) ?
			OnGetText(key, ppropvarNewValue) :
			GetWndRibbon().DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
	}
};

// CButtonCtrl base class for most ribbon controls
//
template <class TWindow>
class CButtonCtrl : public CBaseCtrlImpl<TWindow>
{
public:
	CButtonCtrl(WORD wID) : CBaseCtrlImpl<TWindow>(wID)
	{
	}
	CBitmap m_hbm[4];

	HRESULT SetImage(REFPROPERTYKEY key, HBITMAP hbm, bool bUpdate = false)
	{
		ATLASSERT((k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage));
			
		m_hbm[k_(key) - k_LargeImage].Attach(hbm);

		return bUpdate ?
			GetWndRibbon().InvalidateProperty(GetID(), key) :
			S_OK;
	}

	HRESULT OnGetImage(REFPROPERTYKEY key, PROPVARIANT* ppv)
	{
		ATLASSERT((k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage));

		const INT iImage = k_(key) - k_LargeImage;

		if (m_hbm[iImage].IsNull())
			m_hbm[iImage] = GetWndRibbon().OnRibbonQueryImage(GetID(), key);

		return m_hbm[iImage].IsNull() ?
			E_NOTIMPL :
			SetPropertyVal(key, GetImageCopy(m_hbm[iImage], UI_OWNERSHIP_COPY), ppv);
	}

	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT (nCmdID == GetID());

		return (k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage) ?
			OnGetImage(key, ppropvarNewValue) :
			CBaseCtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
	}
};


///////////////////////////////////////////////////////////////////////////////
// Ribbon collection base classes

// CCallbackProperty class: ribbon callback for each item in a collection
//
struct IPropCollection
{
	virtual HRESULT OnGetItem(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value) = 0 ;
};

class CCallbackProperty : public IUISimplePropertySet
{
public:
	CCallbackProperty(UINT i, IPropCollection* pCollection) : m_Index(i), m_pCollection(pCollection)
	{ }

	const UINT m_Index;
	IPropCollection* m_pCollection;

	// IUISimplePropertySet method.
	STDMETHODIMP GetValue(REFPROPERTYKEY key, PROPVARIANT *value)
	{
		return m_pCollection->OnGetItem(m_Index, key, value);
	}

	// IUnknown methods.
	STDMETHODIMP_(ULONG) AddRef()
	{
		return 1;
	}

	STDMETHODIMP_(ULONG) Release()
	{
		return 1;
	}

	STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
	{
		if ((iid == __uuidof(IUnknown)) || (iid == __uuidof(IUISimplePropertySet)))
		{
			*ppv = this;
			return S_OK;
		}
		else
		{
			return E_NOINTERFACE;
		}
	}
};

// CCallbackCollection: base class for all RibbonUI collections
//
template <class TCollection, class TItemType>
class CCallbackCollection : public IPropCollection
{
	typedef CCallbackCollection<TCollection, TItemType> thisClass;

public:
	CCallbackCollection(size_t nSize) 
	{

		for (size_t i = 0; i < nSize; i++)
			m_arrItems.Add(CAutoPtr<TItemType>(new TItemType(i, this)));
	}

	~CCallbackCollection()
	{
	}

	
	HRESULT Resize(size_t size, bool bUpdate = false)
	{
		if(size > m_arrItems.GetCount())
		{
			for (size_t i = m_arrItems.GetCount(); i < size; i++)
			{
				m_arrItems.Add(CAutoPtr<TItemType>(new TItemType(i,this))); 
			}
		} 
		else if (size < m_arrItems.GetCount())
		{
			m_arrItems.RemoveAt(size -1, m_arrItems.GetCount() - size);
		}
		return bUpdate ? static_cast<TCollection*>(this)->InvalidateItems() : S_OK;
	}

	HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
							 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* /*ppropvarNewValue*/)
	{	
		nCmdID;   // avoid level 4 warning

		HRESULT hr = E_NOTIMPL;
		switch (k_(key))
		{
		case k_ItemsSource:
			{
				ATL::CComQIPtr<IUICollection> pIUICollection(ppropvarCurrentValue->punkVal);
				ATLASSERT(pIUICollection);
				hr = pIUICollection->Clear();
				for (UINT i = 0; i < m_arrItems.GetCount(); i++)
				{
					if FAILED(hr = pIUICollection->Add(m_arrItems[i]))
						break;
				}
				ATLASSERT(SUCCEEDED(hr));
			}
			break;
		}

		return hr;
	}
	virtual HRESULT OnGetItem(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value)
	{
#ifdef _DEBUG
		switch (k_(key))
		{
		case k_LabelDescription:
		case k_Keytip:
		case k_Label:
		case k_TooltipDescription: 
		case k_TooltipTitle :
			SetPropertyVal(key, L"Reached default implementation at atlribbon.h", value);
		default:
			break;
		}
		//ATLASSERT(FALSE);
#endif
		return E_NOTIMPL;
	}

// Data members
	CAutoPtrArray<TItemType> m_arrItems;
};

class CTextProperty : public CCallbackProperty
{
public:
	CTextProperty(UINT i, IPropCollection* pCollection) : CCallbackProperty(i, pCollection)
	{
		m_strText = L"";
	}
	// IUISimplePropertySet method.
	STDMETHODIMP GetValue(REFPROPERTYKEY key, PROPVARIANT *value)
	{
		if( k_Label == k_(key) &&  L"" != m_strText)
		{
			HRESULT hr = SetPropertyVal(key, (LPCWSTR)m_strText, value);
			return hr;
		}

		return CCallbackProperty::GetValue(key, value);
	}

	void SetLabel(LPCWSTR text)
	{
		m_strText = text;
	}

	LPCWSTR GetLabel() const {return m_strText;}

private:
	Text m_strText;
};

class CCategoryProperty : public CTextProperty
{
public:
	CCategoryProperty(UINT i, IPropCollection* pCollection) : 
		CTextProperty(i, pCollection), m_nCategoryId(UI_COLLECTION_INVALIDINDEX)
	{
	}
	// IUISimplePropertySet method.
	STDMETHODIMP GetValue(REFPROPERTYKEY key, PROPVARIANT *value)
	{
		if( k_CategoryId == k_(key))
		{
			if (UI_COLLECTION_INVALIDINDEX != m_nCategoryId)
			{
				HRESULT hr = SetPropertyVal(key, m_nCategoryId, value);
				return hr;
			}
		}
		return CTextProperty::GetValue(key, value);
	}
	void SetCategoryId(UINT32 id)
	{
		m_nCategoryId = id;
	}
private:
	UINT32 m_nCategoryId;
};

// CCategoryCollection: handles categories and collection resizing
//
template <class TCtrl, class TItemType = CCategoryProperty>
class CCategoryCollection : public CCallbackCollection<CCategoryCollection<TCtrl, TItemType>, TItemType>
{
	typedef CCategoryCollection<TCtrl, TItemType> thisClass;
	typedef CCallbackCollection<CCategoryCollection<TCtrl, TItemType>, TItemType> baseClass;
public:

	//Class for storing category items
	class CCategoryHeadersCollection : public CCallbackCollection<CCategoryHeadersCollection, CTextProperty>
	{
		typedef CCallbackCollection<CCategoryHeadersCollection, CCategoryProperty> baseCategoryClass;
	public:
		CCategoryHeadersCollection(CCategoryCollection* pParent, size_t nItems) : 
			CCallbackCollection(nItems), m_pParent(pParent)
		{
		}

		virtual HRESULT OnGetItem(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value)
		{
			HRESULT hr = S_OK;

			switch (k_(key))
			{
			case k_Label:
			{
				TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(m_pParent)->GetWndRibbon();
				Text strCat = ribbon.OnRibbonQueryCategoryText(static_cast<TCtrl*>(m_pParent)->GetID(), uIndex);
				m_arrItems[uIndex]->SetLabel(strCat);
				hr = SetPropertyVal(key, (LPCWSTR)strCat, value);			
			}
			break;
			
			default:
				return CCallbackCollection::OnGetItem(uIndex, key, value);				
			}

			return hr;

		
		}
	private:
		CCategoryCollection<TCtrl, TItemType> * m_pParent;
	};

	typedef thisClass Collection;

	CCategoryCollection(size_t items, size_t categories) : 
		CCallbackCollection(items), m_categoryHeadersItems(this, categories)
	{
	}

// Operations
	HRESULT SetItemCategory(UINT uItem, UINT uCat, bool bUpdate = false)
	{
		ATLASSERT((uItem <= m_categoryItems.m_arrItems.GetCount()) && (uCat <= m_arrItems.GetCount()));

		m_arrItems[uItem].SetCategoryId(uCat);

		return bUpdate ? m_categoryItems.InvalidateItems() : S_OK;
	}

	HRESULT SetCategoryText(UINT uCat, LPCWSTR sText, bool bUpdate = false)
	{
		//ATLASSERT(uCat < t_categories);
		ATLASSERT(uCat <= m_categoryHeadersItems.m_arrItems.GetCount());

		m_categoryHeadersItems.m_arrItems[uCat].SetLabel(sText);

		return bUpdate ? InvalidateCategories() : S_OK;
	}

// Implementation
	virtual HRESULT OnGetItem(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value)
	{
		HRESULT hr = S_OK;

		switch (k_(key))
		{
		
		case k_CategoryId:
			{
			TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
			UINT32 uCat = ribbon.OnRibbonQueryItemCategory(static_cast<TCtrl*>(this)->GetID(), uIndex);
			m_arrItems[uIndex]->SetCategoryId(uCat);
			hr = SetPropertyVal(key, uCat, value);
			}
			break;
		default:
			ATLASSERT(FALSE);
			break;
		}

		return hr;
	}

	
	HRESULT InvalidateItems()
	{
		return static_cast<TCtrl*>(this)->Invalidate(UI_PKEY_ItemsSource);
	}

	HRESULT InvalidateCategories()
	{
		return static_cast<TCtrl*>(this)->Invalidate(UI_PKEY_Categories);
	}

	HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
							 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT(nCmdID == static_cast<TCtrl*>(this)->GetID());
		nCmdID;   // avoid level 4 warning

		HRESULT hr (E_NOTIMPL);

		switch (k_(key))
		{		
		case k_Categories:
			{
				ATL::CComQIPtr<IUICollection> pIUICategory(ppropvarCurrentValue->punkVal);
				ATLASSERT(pIUICategory.p);
				hr = pIUICategory->Clear();
				for (UINT i = 0; i < m_categoryHeadersItems.m_arrItems.GetCount(); i++)
				{
					if FAILED(hr = pIUICategory->Add(m_categoryHeadersItems.m_arrItems[i]))
						break;
				}
				ATLASSERT(SUCCEEDED(hr));
			}
			break;
		default:
			hr = baseClass::DoUpdateProperty(nCmdID, key,  ppropvarCurrentValue, ppropvarNewValue);
		}

		return hr;
	}

	private:
		CCategoryHeadersCollection m_categoryHeadersItems;
};


// CTextCollection: handles item labels and selection
//TODO: remove this class
template <class TCtrl, class TItemType = CCategoryProperty>
class CTextCollection : public CCategoryCollection<TCtrl, TItemType>
{
	typedef CTextCollection<TCtrl, TItemType> thisClass;
	typedef CCategoryCollection<TCtrl, TItemType> baseClass;
public:
	typedef thisClass TextCollection;

	CTextCollection(size_t items, size_t categories) : baseClass(items, categories), m_uSelected(UI_COLLECTION_INVALIDINDEX)
	{ }

	UINT m_uSelected;

	// Operations
	HRESULT SetItemText(UINT uItem, LPCWSTR sText, bool bUpdate = false)
	{
		ATLASSERT(uItem <= m_arrItems.GetCount());
		m_arrItems[uItem]->SetLabel(sText);

		return bUpdate ? InvalidateItems() : S_OK;
	}

	UINT GetSelected()
	{
		return m_uSelected;
	}

	HRESULT Select(UINT uItem, bool bUpdate = false)
	{
		ATLASSERT((uItem <=  m_arrItems.GetCount()) || (uItem == UI_COLLECTION_INVALIDINDEX));

		m_uSelected = uItem;

		TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
		return bUpdate ?
			ribbon.SetProperty(static_cast<TCtrl*>(this)->GetID(), UI_PKEY_SelectedItem, uItem) : 
			S_OK;
	}

// Implementation
	virtual HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
	{
		ATLASSERT(uItem <= m_arrItems.GetCount());

		if (k_(key) == k_Label)
		{
				
			TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
			Text strText = ribbon.OnRibbonQueryItemText( static_cast<TCtrl*>(this)->GetID(), uItem);
			this->SetItemText(uItem, strText);
			return SetPropertyVal(key, (LPCWSTR)strText, value);
		}
		else
		{
			return baseClass::OnGetItem(uItem, key, value);
		}
	}

	HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
							 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT(nCmdID == static_cast<TCtrl*>(this)->GetID());

		if (k_(key) == k_SelectedItem)
		{
			TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
			UINT uSel = UI_COLLECTION_INVALIDINDEX;
			if ((m_uSelected == UI_COLLECTION_INVALIDINDEX) &&
				ribbon.OnRibbonQuerySelectedItem(static_cast<TCtrl*>(this)->GetID(), uSel))
				m_uSelected = uSel;

			return SetPropertyVal(key, m_uSelected, ppropvarNewValue);
		}
		else
		{
			return baseClass::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
		}
	}
};




class CImageProperty : public CCategoryProperty
{
public:
	CImageProperty(UINT i, IPropCollection* pCollection) : 
		CCategoryProperty(i, pCollection)
	{
	}
	// IUISimplePropertySet method.
	STDMETHODIMP GetValue(REFPROPERTYKEY key, PROPVARIANT *value)
	{
		if( k_ItemImage == k_(key))
		{
			if (NULL != m_image)
			{
				HRESULT hr = SetPropertyVal(key, GetImageCopy(m_image, UI_OWNERSHIP_COPY), value);
				return hr;
			}
		}
		return CCategoryProperty::GetValue(key, value);
	}
	void SetImage(HBITMAP hImage)
	{
		m_image = hImage;
	}
	HBITMAP GetImage() const {return m_image;}
private:
	CBitmap m_image;
};


// CImageCollection: handles item image
//
template <class TCtrl, class TItemType = CImageProperty>
class CImageCollection : public CTextCollection<TCtrl, TItemType>
{
	typedef CImageCollection<TCtrl, TItemType> thisClass;
	typedef CTextCollection<TCtrl, TItemType> baseClass;
public:
	typedef thisClass ItemCollection;
	
	CImageCollection(size_t items, size_t categories = 0 ) : CTextCollection(items, categories)
	{
	}

	// Operations
	HRESULT SetItemImage(UINT uIndex, HBITMAP hbm, bool bUpdate = false)
	{
		//ATLASSERT(uIndex < t_items);
		ATLASSERT(uIndex <= m_arrItems.GetCount());

		m_arrItems[uIndex]->SetImage(hbm);

		return bUpdate ? InvalidateItems() : S_OK;
	}

// Implementation
	virtual HRESULT OnGetItem(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value)
	{
		ATLASSERT(uIndex <= m_arrItems.GetCount());

		if (k_(key) == k_ItemImage)
		{
			if (NULL == m_arrItems[uIndex]->GetImage())
			{
				TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
				HBITMAP hBmp = ribbon.OnRibbonQueryItemImage(static_cast<TCtrl*>(this)->GetID(), uIndex);
				m_arrItems[uIndex]->SetImage(hBmp);
			}
			return NULL == m_arrItems[uIndex]->GetImage() ?
				E_NOTIMPL :
				SetPropertyVal(key,  GetImageCopy(m_arrItems[uIndex]->GetImage(), UI_OWNERSHIP_COPY), value);
		}
		else
		{
			return baseClass::OnGetItem(uIndex, key, value);
		}
	}
};


class CCommandProperty : public CCategoryProperty
{
public:
	CCommandProperty(UINT i, IPropCollection* pCollection) : 
		CCategoryProperty(i, pCollection), m_aCmdType(UI_COMMANDTYPE_UNKNOWN)
	{
	}
	// IUISimplePropertySet method.
	STDMETHODIMP GetValue(REFPROPERTYKEY key, PROPVARIANT *value)
	{
		HRESULT hr = E_FAIL;
		switch (k_(key))
		{
		case k_CommandId:
			if (0 != m_auCmdId)
			{
				hr = SetPropertyVal(key, m_auCmdId, value);
				return hr;
			}			
		case k_CommandType:
			if (UI_COMMANDTYPE_UNKNOWN != m_aCmdType)
			{	
				hr = SetPropertyVal(key, UINT32(m_aCmdType), value);
				return hr;
			}
		}
		return CCategoryProperty::GetValue(key, value);
	}
	void SetCommandId(UINT32 id)
	{
		m_auCmdId = id;
	}
	UINT32 GetCommandId()
	{
		return m_auCmdId;
	}
	void SetCommandType(BYTE id)
	{
		m_aCmdType = id;
	}
	BYTE GetCommandType()
	{
		return m_aCmdType;
	}
private:
	UINT32 m_auCmdId;
	BYTE m_aCmdType;
};

// CommandCollectionImpl: handles RibbonUI command collection controls
//
template <class TCtrl, class TItemType = CCommandProperty>
class CCommandCollectionImpl : public CCategoryCollection<TCtrl, TItemType>
{
	typedef CCommandCollectionImpl<TCtrl, TItemType> thisClass;
	typedef CCategoryCollection<TCtrl, TItemType> baseClass;
public:
	

	CCommandCollectionImpl(size_t items, size_t categories = 0) : 
		CCategoryCollection(items, categories)
	{
	}

	// Operations
	HRESULT SetItemCommand(UINT uItem, UINT32 uCommandID, bool bUpdate = false)
	{
		//ATLASSERT(uItem < t_items);
		ATLASSERT(uCat < <= m_arrItems.GetCount());

		if (uCommandID == m_arrItems[uItem].GetCommandId())
			return S_OK;

		TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();

		m_arrItems[uItem].SetCommandId(uCommandID);
		if (uCommandID != 0)
			ribbon.UIAddRibbonElement(uCommandID);

		return bUpdate ? InvalidateItems() : S_OK;
	}

	HRESULT SetItemCommandType(UINT uItem, UI_COMMANDTYPE type, bool bUpdate = false)
	{
		ATLASSERT(uCat <= m_arrItems.GetCount());

		m_arrItems[uItem]SetCommandType((BYTE)type);

		return bUpdate ? InvalidateItems() : S_OK;
	}

// Implementation
	virtual HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
	{
		ATLASSERT(uItem < t_items);
		TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();

		HRESULT hr = E_FAIL;
		switch (k_(key))
		{
		case k_CommandId:
			SetItemCommand(uItem, ribbon.OnRibbonQueryItemCommand(static_cast<TCtrl*>(this)->GetID(), uItem));
			hr = SetPropertyVal(key, m_auCmd[uItem], value);
			break;
		case k_CommandType:
			SetItemCommandType(uItem, ribbon.OnRibbonQueryItemCommandType(static_cast<TCtrl*>(this)->GetID(), uItem));
			hr = SetPropertyVal(key, UINT32(m_aCmdType[uItem]), value);
			break;
		case k_CategoryId:
		default:
			hr = baseClass::OnGetItem(uItem, key, value);
			break;
		}

		return hr;
	}

	HRESULT Select(UINT /*uItem*/, bool /*bUpdate*/ = false)
	{
		ATLASSERT(FALSE);
		return S_OK;
	}
};

// SimpleCollectionImpl: collection class for ribbon simple collection controls
//
template <class TCtrl, class TItemType = CCallbackProperty>
class CSimpleCollectionImpl : public CCallbackCollection<CSimpleCollectionImpl<TCtrl, TItemType>, TItemType>
{
	typedef CSimpleCollectionImpl<TCtrl, TItemType> thisClass;
	typedef CCallbackCollection<CSimpleCollectionImpl<TCtrl, TItemType>, TItemType> baseClass;
public:
	typedef CCallbackCollection<thisClass, CCallbackProperty> CollectionBase;
	typedef thisClass SimpleCollection;

	CSimpleCollectionImpl(size_t nSize, UI_COMMANDTYPE CommandType = UI_COMMANDTYPE_ACTION) : 
		CCallbackCollection(nSize), m_uiCmdType(CommandType)
	{
	}

// Implementation
	HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
	{
		ATLASSERT(uCat <= m_arrItems.GetCount());
		TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();

		HRESULT hr = E_NOTIMPL;
		switch (k_(key))
		{
		case k_ItemImage:
			if (HBITMAP hbm = ribbon.OnRibbonQueryItemImage(static_cast<TCtrl*>(this)->GetID(), uItem))
				hr = SetPropertyVal(key, GetImageCopy(hbm, UI_OWNERSHIP_TRANSFER), value);
			break;
		case k_Label:
			if (LPCWSTR sText = ribbon.OnRibbonQueryItemText(static_cast<TCtrl*>(this)->GetID(), uItem))
				hr = SetPropertyVal(key, (LPCWSTR)sText, value);
			break;
		case k_CommandType:
			hr = SetPropertyVal(key, m_uiCmdType, value);
			break;
		case k_CommandId:
			hr = SetPropertyVal(key, ribbon.OnRibbonQueryItemCommand(static_cast<TCtrl*>(this)->GetID(), uItem), value);
			break;
		case k_CategoryId:
			hr = SetPropertyVal(key, UI_COLLECTION_INVALIDINDEX, value);
			break;
		default:
			ATLASSERT(FALSE);
			break;
		}

		return hr;
	}
private:
	UI_COMMANDTYPE m_uiCmdType;
};


///////////////////////////////////////////////////////////////////////////////
// Ribbon collection control classes

// CollectionCtrlImpl: specializable class for ribbon collection controls
//
template <class TWindow, class TCollection>
class CCollectionCtrlImpl : public CButtonCtrl<TWindow>, public TCollection
{
	typedef CCollectionCtrlImpl<TWindow, TCollection> thisClass;
	typedef CButtonCtrl<TWindow> baseButtonClass;
public:

	CCollectionCtrlImpl(WORD wID, size_t items, size_t categories = 0) : baseButtonClass(wID),
		TCollection(items, categories)
	{
	}

	// Implementation
	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT(nCmdID == GetID());
		ATLASSERT(ppropvarNewValue);

		HRESULT hr = TCollection::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
		if FAILED(hr)
			hr = baseButtonClass::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);

		return hr;
	}

	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* /*pCommandExecutionProperties*/)
	{
		ATLASSERT (nCmdID == GetID());
		nCmdID; // avoid level4 warning

		if (key == NULL) // gallery button pressed
		{
			GetWndRibbon().OnRibbonItemSelected(GetID(), UI_EXECUTIONVERB_EXECUTE, UI_COLLECTION_INVALIDINDEX);
			return S_OK;
		}

		ATLASSERT(k_(*key) == k_SelectedItem);
		ATLASSERT(ppropvarValue);

		HRESULT hr = S_OK;
		UINT32 uSel = 0xffff;
		hr = UIPropertyToUInt32(*key, *ppropvarValue, &uSel);

		if (SUCCEEDED(hr))
		{
			if (GetWndRibbon().OnRibbonItemSelected(GetID(), verb, uSel))
				TCollection::Select(uSel);
		}

		return hr;
	}
};

// CToolbarGalleryCtrlImpl: base class for ribbon toolbar gallery controls
//
template <class TWindow>
class CToolbarGalleryCtrlImpl : 
	public CCollectionCtrlImpl<TWindow, CCommandCollectionImpl<CToolbarGalleryCtrlImpl<TWindow> > >
{
protected:
	UINT m_nToolbarID;
public:
	CToolbarGalleryCtrlImpl(WORD wID, UINT nToolbarId, size_t items, size_t categories = 0) :
		m_nToolbarID(nToolbarId),
		CCollectionCtrlImpl<TWindow, CCommandCollectionImpl<CToolbarGalleryCtrlImpl<TWindow > > >(wID, items, categories)
	{
		CResource tbres;
		ATLVERIFY(tbres.Load(RT_TOOLBAR, nToolbarId));
		_AtlToolBarData* pData = (_AtlToolBarData*)tbres.Lock();
		ATLASSERT(pData);
		ATLASSERT(pData->wVersion == 1);

		WORD* pItems = pData->items();
		INT j = 0;
		for (int i = 0; (i < pData->wItemCount) && (j < t_size); i++)
		{
			if (pItems[i] != 0)
			{
				m_aCmdType[j] = UI_COMMANDTYPE_ACTION;
				m_auCmd[j++] = pItems[i];
			}
		}

		if (j < t_size)
			Resize(j);
	}

	HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
	{
		ATLASSERT(uItem < m_size);
		ATLASSERT(m_auCmd[uItem]);

		HRESULT hr = E_FAIL;
		switch (k_(key))
		{
		case k_CommandId:
			hr = SetPropertyVal(key, m_auCmd[uItem], value);
			break;
		case k_CommandType:
			hr = SetPropertyVal(key, UINT32(m_aCmdType[uItem]), value);
			break;
		case k_CategoryId:
			hr = SetPropertyVal(key, UI_COLLECTION_INVALIDINDEX, value);
			break;
		default:
			ATLASSERT(FALSE);
			break;
		}

		return hr;
	}
};


// SimpleCollectionCtrlImpl: base class for simple gallery and listbox controls
//
template <class TWindow>
class CSimpleCollectionCtrlImpl : 
		public CButtonCtrl<TWindow>,
		public CSimpleCollectionImpl<CSimpleCollectionCtrlImpl<TWindow> >
{
	typedef CSimpleCollectionCtrlImpl<TWindow> thisClass;
	typedef  CSimpleCollectionImpl<CSimpleCollectionCtrlImpl<TWindow> > baseClass;
public:
	typedef thisClass SimpleCollection;

	CSimpleCollectionCtrlImpl(WORD wID, size_t size, UI_COMMANDTYPE CommandType = UI_COMMANDTYPE_ACTION) : 
		m_uSelected(0),
		CButtonCtrl<TWindow>(wID),
		CSimpleCollectionImpl(wID, size, CommandType)
	{ }

	UINT m_uSelected;

	HRESULT Select(UINT uItem, bool bUpdate = false)
	{
		ATLASSERT(uCat <= m_arrItems.GetCount() || (uItem == UI_COLLECTION_INVALIDINDEX));

		m_uSelected = uItem;

		return bUpdate ? 
			GetWndRibbon().SetProperty(GetID(), UI_PKEY_SelectedItem, uItem) : 
			S_OK;
	}

	// Implementation
	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT(nCmdID == GetID());
		ATLASSERT(ppropvarNewValue != NULL);

		HRESULT hr(E_NOTIMPL);
		switch (k_(key))
		{		
		case k_SelectedItem:
			hr = SetPropertyVal(UI_PKEY_SelectedItem, m_uSelected, ppropvarNewValue);
			break;
		default:
			hr = baseClass::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
			if(E_NOTIMPL == hr)
				hr = CButtonCtrl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
			break;
		}

		return hr;
	}

	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* /*pCommandExecutionProperties*/)
	{
		ATLASSERT (nCmdID == GetID());
		nCmdID;   // avoid level 4 warning
		
		HRESULT hr = S_OK;
		if (key == NULL) // gallery button pressed
		{
			GetWndRibbon().OnRibbonItemSelected(GetID(), UI_EXECUTIONVERB_EXECUTE, UI_COLLECTION_INVALIDINDEX);
			return hr;
		}
		ATLASSERT(k_(*key) == k_SelectedItem);
		ATLASSERT(ppropvarValue);

		if SUCCEEDED(hr = UIPropertyToUInt32(*key, *ppropvarValue, &m_uSelected))
			GetWndRibbon().OnRibbonItemSelected(GetID(), verb, m_uSelected);

		return hr;
	}
};

class CRibbonIUIFramework
{
	//Data
protected:
	ATL::CComPtr<IUIFramework> m_pIUIFramework;
public:
	CRibbonIUIFramework()  
	{
	}

	~CRibbonIUIFramework()
	{
	}

	operator IUIFramework*() const
	{
		return m_pIUIFramework;
	}

	IUIFramework* GetIUIFrameworkPtr()
	{
		return m_pIUIFramework;
	}

	template <typename I>
	I* GetRibbonViewPtr(UINT32 uID)
	{
		ATLASSERT(m_pIUIFramework);
		ATL::CComPtr<I> pI;
		return m_pIUIFramework->GetView(uID, __uuidof(I), (void**) &pI) == S_OK ?
			pI : 
			NULL;
	}

	IUIRibbon* GetRibbonPtr()
	{
		return GetRibbonViewPtr<IUIRibbon>(0);
	}

	IUIContextualUI* GetMenuPtr(UINT32 uID)
	{
		ATLASSERT(uID);
		return GetRibbonViewPtr<IUIContextualUI>(uID);
	}

	
	UINT GetRibbonHeight()
	{
		ATLASSERT(GetRibbonPtr());

		UINT32 cy = 0;
		if (ATL::CComPtr<IUIRibbon> pIUIRibbon = GetRibbonPtr())
			pIUIRibbon->GetHeight(&cy);
		return cy;
	}
		
	
// QAT dock states
	UI_CONTROLDOCK GetQATDock()
	{
		ATLASSERT(GetIUIFrameworkPtr());		

		UINT32 uDock = 0;
		PROPVARIANT propvar;
		ATL::CComQIPtr<IPropertyStore>pIPS(GetRibbonPtr());

		if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(UI_PKEY_QuickAccessToolbarDock, &propvar)) &&
			SUCCEEDED(UIPropertyToUInt32(UI_PKEY_QuickAccessToolbarDock, propvar, &uDock)))
				return (UI_CONTROLDOCK)uDock;

		ATLASSERT(FALSE); // something was wrong
		return (UI_CONTROLDOCK)0;
	}

	bool SetQATDock(UI_CONTROLDOCK dockState)
	{
		ATLASSERT(GetIUIFrameworkPtr());
		
		PROPVARIANT propvar;
		ATLVERIFY(SUCCEEDED(SetPropertyVal(UI_PKEY_QuickAccessToolbarDock, dockState, &propvar)));

		ATL::CComQIPtr<IPropertyStore>pIPS(GetRibbonPtr());
		if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(UI_PKEY_QuickAccessToolbarDock, propvar)))
		{
			pIPS->Commit();
			return true;
		}

		ATLASSERT(FALSE); // something was wrong
		return false;
	}

// Ribbon display states
	bool GetRibbonDisplayState(REFPROPERTYKEY key)
	{
		ATLASSERT(GetIUIFrameworkPtr());
		ATLASSERT((k_(key) == k_Viewable) || (k_(key) == k_Minimized));

		PROPVARIANT propvar;
		ATL::CComQIPtr<IPropertyStore>pIPS( GetRibbonPtr());

		if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(key, &propvar)))
		{
			BOOL bState = FALSE;
			if SUCCEEDED(UIPropertyToBoolean(key, propvar, &bState))
				return (bState != FALSE);
		}

		ATLASSERT(FALSE); // something was wrong
		return false;
	}

	bool SetRibbonDisplayState(REFPROPERTYKEY key, bool bState = true)
	{
		ATLASSERT(GetIUIFrameworkPtr());
		ATLASSERT((k_(key) == k_Viewable) || (k_(key) == k_Minimized));

		PROPVARIANT propvar;
		ATLVERIFY(SUCCEEDED(SetPropertyVal(key, bState, &propvar)));

		ATL::CComQIPtr<IPropertyStore>pIPS(GetRibbonPtr());

		if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(key, propvar)))
		{
			pIPS->Commit();
			return true;
		}

		ATLASSERT(FALSE); // something was wrong
		return false;
	}

	bool IsRibbonMinimized()
	{
		return GetRibbonDisplayState(UI_PKEY_Minimized);
	}

	bool MinimizeRibbon(bool bMinimize = true)
	{
		return SetRibbonDisplayState(UI_PKEY_Minimized, bMinimize);
	}

	bool IsRibbonHidden()
	{
		return !GetRibbonDisplayState(UI_PKEY_Viewable);
	}

	bool HideRibbon(bool bHide = true)
	{
		return SetRibbonDisplayState(UI_PKEY_Viewable, !bHide);
	}

// Ribbon colors
	UI_HSBCOLOR GetRibbonColor(REFPROPERTYKEY key)
	{
		ATLASSERT(GetIUIFrameworkPtr());
		ATLASSERT((k_(key) >= k_GlobalBackgroundColor) && (k_(key) <= k_GlobalTextColor));

		PROPVARIANT propvar;
		ATL::CComQIPtr<IPropertyStore>pIPS(GetIUIFrameworkPtr());

		if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(key, &propvar)))
		{
			UINT32 color = 0;
			if SUCCEEDED(UIPropertyToUInt32(key, propvar, &color))
				return color;
		}

		ATLASSERT(FALSE); // something was wrong
		return 0;
	}

	bool SetRibbonColor(REFPROPERTYKEY key, UI_HSBCOLOR color)
	{
		ATLASSERT(GetIUIFrameworkPtr());
		ATLASSERT((k_(key) >= k_GlobalBackgroundColor) && (k_(key) <= k_GlobalTextColor));

		PROPVARIANT propvar;
		ATLVERIFY(SUCCEEDED(SetPropertyVal(key, color, &propvar)));

		ATL::CComQIPtr<IPropertyStore>pIPS(GetIUIFrameworkPtr());

		if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(key, propvar)))
		{
			pIPS->Commit();
			return true;
		}

		ATLASSERT(FALSE); // something was wrong
		return false;
	}

// Ribbon modes
	HRESULT SetRibbonModes(INT32 iModes)
	{	
		return GetIUIFrameworkPtr()->SetModes(iModes);
	}


// Ribbon context menu
	bool HasRibbonMenu(UINT32 uID)
	{
		ATL::CComPtr<IUIContextualUI> pI = GetMenuPtr(uID);
		return pI != NULL;
	}

	HRESULT TrackRibbonMenu(UINT32 uID, INT32 x, INT32 y)
	{
		ATLASSERT(HasRibbonMenu(uID));

		return GetIUIFrameworkPtr() ?
			ATL::CComPtr<IUIContextualUI>(GetMenuPtr(uID))->ShowAtLocation(x, y) :
			E_FAIL;
	}

	HRESULT TrackRibbonMenu(UINT32 uID, LPARAM lParam)
	{
		return TrackRibbonMenu(uID, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
	}

	// Elements setting helpers
	HRESULT InvalidateCtrl(UINT32 nID)
	{
		return GetIUIFrameworkPtr() ?
			GetIUIFrameworkPtr()->InvalidateUICommand(nID, UI_INVALIDATIONS_ALLPROPERTIES, NULL) :
			E_FAIL;
	}

	HRESULT InvalidateProperty(UINT32 nID, REFPROPERTYKEY key, UI_INVALIDATIONS flags = UI_INVALIDATIONS_PROPERTY)
	{
		return GetIUIFrameworkPtr() ?
			GetIUIFrameworkPtr()->InvalidateUICommand(nID, flags, &key) :
			E_FAIL;
	}

	
	template <typename V>
	HRESULT SetProperty(WORD wID, REFPROPERTYKEY key, V val)
	{
		if (GetIUIFrameworkPtr())
		{
			PROPVARIANT var;
			if (SUCCEEDED(RibbonUI::SetPropertyVal(key, val, &var)))
			{
				return SetProperty(wID, key, var);
			}
			return E_INVALIDARG;
		}
		else
		{
			return E_FAIL;
		}
	}

	template <>
	HRESULT SetProperty(WORD nID, REFPROPERTYKEY key, PROPVARIANT var)
	{
		return GetIUIFrameworkPtr() ?
			GetIUIFrameworkPtr()->SetUICommandProperty(nID, key, var) :
			E_FAIL;
	}

};

template<class TDerived, class TIUIFramework = CRibbonIUIFramework>
class CRibbonCtrlBaseT : public TIUIFramework
{
private:
	CWindow m_hWndParent;
	ATL::CComPtr<IUIApplication> m_pApplication;
	HGLOBAL m_hgRibbonSettings;
	bool m_bRibbonUI;
public:
	 
	CRibbonCtrlBaseT() : m_hgRibbonSettings(NULL), m_bRibbonUI(false)
	{
	}

	~CRibbonCtrlBaseT()
	{
		if(NULL != m_pIUIFramework)
		{
			m_pIUIFramework.Release();
		}

		::GlobalFree(m_hgRibbonSettings);
	}


	bool IsRibbonUI()
	{
		return m_bRibbonUI;
	}

	void SetIUIApplication(IUIApplication * pApp)
	{
		ATLASSERT(NULL != pApp);
		m_pApplication = pApp;
	}

	HRESULT CreateRibbon(HWND hWndParent, IUIApplication * pApp,  LPCWSTR sResName = L"APPLICATION_RIBBON")
	{
		ATLASSERT(NULL != hWndParent);
		ATLASSERT(NULL != pApp);
		
		m_hWndParent = hWndParent;
		m_pApplication = pApp;
		
		ATLASSERT(m_hWndParent.IsWindow());
		HRESULT hr;
		if(NULL == m_pIUIFramework)
		{
				if (RunTimeHelper::IsRibbonUIAvailable())
				{
					hr = m_pIUIFramework.CoCreateInstance(CLSID_UIRibbonFramework);
				}
				else
				{
					ATLTRACE(L"Ribbon UI not available\n");
					return S_FALSE;
				}

			if (S_OK != hr)
			{
				ATLTRACE(L"Ribbon construction failed\n");
				return hr;
			}

		}

		hr = m_pIUIFramework->Initialize(m_hWndParent, m_pApplication);

		if (S_OK == hr)
			hr = m_pIUIFramework->LoadUI(ModuleHelper::GetResourceInstance(), sResName);
			
		return hr;
	}

	HRESULT DestroyRibbon()
	{
		ATLASSERT(GetIUIFrameworkPtr() && IsRibbonUI());
		ATLASSERT(m_hWndParent.IsWindow());

		HRESULT hRes = m_pIUIFramework->Destroy();
		if (!RunTimeHelper::IsWin7())
			m_hWndParent.SetWindowRgn(NULL, TRUE); // Vista Basic bug workaround
		return hRes;
	}

	UI_CONTEXTAVAILABILITY OnRibbonQueryTabAvail(UINT nCmdID)
	{	
		return UI_CONTEXTAVAILABILITY_NOTAVAILABLE ;
	}

	void CommandExecute(UINT32 uCmd, LPARAM lParam = 0)
	{
		ATLASSERT(false);
	}

	bool OnRibbonItemSelected(UINT32 uCtrlID, UI_EXECUTIONVERB verb, UINT32 uItem)
	{
		static_cast<TDerived*>(this)->CommandExecute(MAKELONG(uCtrlID, verb), uItem);
		return true;
	}

	void OnRibbonColorCtrlExecute(UINT32 uCtrlID, UI_EXECUTIONVERB verb, UI_SWATCHCOLORTYPE uType, COLORREF color)
	{
		switch(uType)
		{
		case UI_SWATCHCOLORTYPE_RGB:
			break;
		case UI_SWATCHCOLORTYPE_AUTOMATIC:
			color = ::GetSysColor(COLOR_WINDOWTEXT);
			break;
		case UI_SWATCHCOLORTYPE_NOCOLOR:
			color = ::GetSysColor(COLOR_WINDOW);
			break;
		default:
			ATLASSERT(FALSE);
			break;
		}

		static_cast<TDerived*>(this)->CommandExecute(MAKELONG(uCtrlID, verb), color);
	}

	void OnRibbonFontCtrlExecute(UINT32 uCtrlID, UI_EXECUTIONVERB verb, CHARFORMAT2* pcf)
	{
		static_cast<TDerived*>(this)->CommandExecute(MAKELONG(uCtrlID, verb), (LPARAM)pcf);
	}

	void OnRibbonSpinnerCtrlExecute(UINT32 uCtrlID, LONG* pVal)
	{
		static_cast<TDerived*>(this)->CommandExecute(uCtrlID, *pVal);
	}

	void OnRibbonSpinnerCtrlExecute(UINT32 uCtrlID, DOUBLE* pVal)
	{
		static_cast<TDerived*>(this)->CommandExecute(uCtrlID, (LPARAM)pVal);
	}

	void OnRibbonCommandExecute(UINT32 uCmdID)
	{
		static_cast<TDerived*>(this)->CommandExecute(uCmdID);
	}

	bool OnRibbonQueryState(UINT nCmdID, REFPROPERTYKEY key)
	{
		return false;
	}

	HBITMAP OnRibbonQueryImage(UINT nCmdID, REFPROPERTYKEY /*key*/)
	{
		return AtlLoadBitmapImage(nCmdID, LR_CREATEDIBSECTION);
	}

	HBITMAP OnRibbonQueryItemImage(UINT32 uCtrlID, UINT32 uItem)
	{
		PROPERTYKEY key;
		return static_cast<TDerived*>(this)->OnRibbonQueryImage(uCtrlID + 1 + uItem, key);
	}

	
	LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key)
	{
		static WCHAR sText[RIBBONUI_MAX_TEXT] = { 0 };

		if (AtlLoadString(nCmdID, sText, RIBBONUI_MAX_TEXT))
		{
			PWCHAR pTitle = wcschr(sText, L'\n');
			switch (k_(key))
			{
			case k_Keytip:
				if (PWCHAR pAmp = wcschr(sText, L'&'))
					pTitle = pAmp;
				if (pTitle != NULL)
					*(pTitle + 2) = NULL; // fall through
			case k_TooltipTitle:
				return pTitle ? ++pTitle : NULL;
			case k_TooltipDescription:
			case k_LabelDescription:
				if (pTitle != NULL)
					*pTitle = NULL;
				return sText;
			}
		}

		return NULL;
	}

	LPCWSTR OnRibbonQueryItemText(UINT32 uCtrlID, UINT32 uItem)
	{
		return static_cast<TDerived*>(this)->OnRibbonQueryText(uCtrlID + 1 + uItem, UI_PKEY_LabelDescription);
	}

	LPCWSTR OnRibbonQueryComboText(UINT32 /*uCtrlID*/)
	{
		return NULL;
	}

	LPCWSTR OnRibbonQueryCategoryText(UINT32 /*uCtrlID*/, UINT32 /*uCat*/)
	{
		return L"Category";
	}

	UINT32 OnRibbonQueryItemCategory(UINT32 /*uCtrlID*/, UINT32 /*uItem*/)
	{
		return 0;
	}

	UI_COMMANDTYPE OnRibbonQueryItemCommandType(UINT32 /*uCtrlID*/, UINT32 /*uItem*/)
	{
		return UI_COMMANDTYPE_ACTION;
	}

	bool OnRibbonQuerySelectedItem(UINT32 /*uCtrlID*/, UINT32& /*uSel*/)
	{
		return false;
	}

	LPCWSTR OnRibbonQueryRecentItemName(LPCWSTR sPath)
	{
		return ::PathFindFileName(sPath);
	}

	bool OnRibbonQueryFont(UINT /*nId*/, CHARFORMAT2& /*cf*/)
	{
		return false;
	}

	bool OnRibbonQuerySpinnerValue(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/, LONG* /*pVal*/)
	{
		return false;
	}

	bool OnRibbonQueryFloatSpinnerValue(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/, DOUBLE* /*pVal*/)
	{
		return false;
	}

	COLORREF OnRibbonQueryColor(UINT /*nCmdID*/)
	{
		return 0x800080; /*MAGENTA*/
	}

	LPCWSTR OnRibbonQueryColorLabel(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/)
	{
		return NULL;
	}

	COLORREF* OnRibbonQueryColorArray(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/)
	{
		return NULL;
	}

	LPCWSTR* OnRibbonQueryColorTooltips(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/)
	{
		return NULL;
	}

	UINT32 OnRibbonQueryItemCommand(UINT32 uCtrlID, UINT32 uItem)
	{
		return uCtrlID + 1 + uItem;
	}

	// Ribbon persistency
	HGLOBAL & GetRibbonSettings() { return m_hgRibbonSettings; }
	HRESULT operator >>(IStream* pIStream)
	{
		ATLASSERT(GetIUIFrameworkPtr());
		ATLASSERT(pIStream);

		HRESULT hr = E_FAIL;
		if (ATL::CComPtr<IUIRibbon> pIUIRibbon = GetRibbonPtr())
		{
			const LARGE_INTEGER li0 = { 0 };
			pIStream->Seek(li0, STREAM_SEEK_SET, NULL);
			hr = pIUIRibbon->SaveSettingsToStream(pIStream);
			pIStream->Commit(STGC_DEFAULT);
		}

		return hr;
	}

	HRESULT operator <<(IStream* pIStream)
	{
		ATLASSERT(GetIUIFrameworkPtr());
		ATLASSERT(pIStream);

		HRESULT hr = E_FAIL;
		if (ATL::CComPtr<IUIRibbon> pIUIRibbon = GetRibbonPtr())
		{
			const LARGE_INTEGER li0 = { 0 };
			pIStream->Seek(li0, STREAM_SEEK_SET, NULL);
			hr = pIUIRibbon->LoadSettingsFromStream(pIStream);
		}

		return hr;
	}

	void ResetRibbonSettings()
	{
		if (m_hgRibbonSettings != NULL)
		{
			::GlobalFree(m_hgRibbonSettings);
			m_hgRibbonSettings = NULL;
		}
	}

	HRESULT SaveRibbonSettings()
	{
		ATLASSERT(GetIUIFrameworkPtr());
		ATLASSERT(m_hWndParent.IsWindow());

		HRESULT hr = E_FAIL;
		ATL::CComPtr<IStream> pIStream;

		if SUCCEEDED(hr = ::CreateStreamOnHGlobal(m_hgRibbonSettings, FALSE, &pIStream))
			hr = *this >> pIStream;

		if (SUCCEEDED(hr) && (m_hgRibbonSettings == NULL))
			hr = ::GetHGlobalFromStream(pIStream, &m_hgRibbonSettings);

		if FAILED(hr)
			ResetRibbonSettings();

		m_bRibbonUI = false;

		return hr;
	}

	HRESULT RestoreRibbonSettings()
	{
		ATLASSERT(m_hWndParent.IsWindow());
		//It is here according old logic
		//TODO: check this.
		m_bRibbonUI = true;

		if(NULL != m_hgRibbonSettings)
		{
			HRESULT hr = E_FAIL;
			ATL::CComPtr<IStream> pIStream;

			if SUCCEEDED(hr = ::CreateStreamOnHGlobal(m_hgRibbonSettings, FALSE, &pIStream))
			{
				hr = *this << pIStream;
			}

			if FAILED(hr)
				ResetRibbonSettings();

			return hr;
		}
		return S_OK;
	}

	
};

// CRibbonImpl Ribbon implementation class
//
template <class TFrame>
class CRibbonImpl : 
		public CRibbonCtrlBaseT<CRibbonImpl<TFrame>, CRibbonIUIFramework>,
		public CRibbonUpdateUI<TFrame>,
		public ICtrl,
		public IUIApplication,
		public IUICommandHandler
{
	typedef CRibbonImpl<TFrame> thisClass;	
	typedef CRibbonCtrlBaseT<CRibbonImpl, CRibbonIUIFramework> iuiFrameworkClass;	
public:
	typedef thisClass Ribbon;
	typedef TFrame WndRibbon;

	CRibbonImpl()
	{
#ifdef _DEBUG
		m_cRef = 1;
#endif
		pWndRibbon = static_cast<TFrame*>(this);
		SetIUIApplication(this);
	}

	~CRibbonImpl()
	{
	}

	ICtrl& GetRibbonCtrl(UINT)
	{
		return static_cast<ICtrl&>(*this);
	}
	
	
	// Ribbon contextual tab
	UI_CONTEXTAVAILABILITY GetRibbonContextAvail(UINT32 uID)
	{
		ATLASSERT(GetIUIFrameworkPtr());

		PROPVARIANT propvar;
		if (IsRibbonUI() && 
			SUCCEEDED(GetIUIFrameworkPtr()->GetUICommandProperty(uID, UI_PKEY_ContextAvailable, &propvar)))
		{
			UINT uav;
			if (SUCCEEDED(PropVariantToUInt32(propvar, &uav)))
			{
				CUpdateUIBase::UIEnable(uID, uav != UI_CONTEXTAVAILABILITY_NOTAVAILABLE);
				CUpdateUIBase::UISetCheck(uID, uav == UI_CONTEXTAVAILABILITY_ACTIVE);
				return (UI_CONTEXTAVAILABILITY)uav;
			}
		}

		return UI_CONTEXTAVAILABILITY_NOTAVAILABLE;
	}

	HRESULT SetRibbonContextAvail(UINT32 uID, UI_CONTEXTAVAILABILITY cav)
	{
		CUpdateUIBase::UIEnable(uID, cav != UI_CONTEXTAVAILABILITY_NOTAVAILABLE);
		CUpdateUIBase::UISetCheck(uID, cav == UI_CONTEXTAVAILABILITY_ACTIVE);
		
		return SetProperty((WORD)uID, UI_PKEY_ContextAvailable, UINT32(cav));
	}

	// Overrideables
	
	LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key)
	{
		if (k_(key) == k_Label)
			 return UIGetText(nCmdID);
		return iuiFrameworkClass::OnRibbonQueryText(nCmdID, key);
	}

	bool OnRibbonQueryState(UINT nCmdID, REFPROPERTYKEY key)
	{
		DWORD dwState = UIGetState(nCmdID);
		bool bRet = false;
		switch (k_(key))
		{
		case k_BooleanValue:
			bRet = (dwState & UPDUI_CHECKED) == UPDUI_CHECKED;
			break;
		case k_Enabled:
			bRet = (dwState & UPDUI_DISABLED) != UPDUI_DISABLED;
			break;
		default:
			ATLASSERT(FALSE);
			break;
		}

		return bRet;
	}

	UI_CONTEXTAVAILABILITY OnRibbonQueryTabAvail(UINT nCmdID)
	{
		DWORD dwState = UIGetState(nCmdID);
		return ((dwState & UPDUI_DISABLED) == UPDUI_DISABLED) ? 
			UI_CONTEXTAVAILABILITY_NOTAVAILABLE :
			(((dwState & UPDUI_CHECKED) == UPDUI_CHECKED) ? 
				UI_CONTEXTAVAILABILITY_ACTIVE : 
				UI_CONTEXTAVAILABILITY_AVAILABLE);
	}

	
	void CommandExecute(UINT32 uCmd, LPARAM lParam = 0)
	{
		static_cast<TFrame*>(this)->PostMessage(WM_COMMAND, uCmd, lParam);
	}


// Interfaces
	// IUIApplication
	STDMETHODIMP OnViewChanged(UINT32, UI_VIEWTYPE, IUnknown*, UI_VIEWVERB verb, INT32)
	{
		switch (verb)
		{			
		case UI_VIEWVERB_CREATE:
			RestoreRibbonSettings();
			break;
		case UI_VIEWVERB_SIZE:
			static_cast<TFrame*>(this)->UpdateLayout(FALSE);
			break;
		case UI_VIEWVERB_DESTROY:
			SaveRibbonSettings();			
			break;
		}

		return S_OK;
	}

	STDMETHODIMP OnCreateUICommand(UINT32 nCmdID, UI_COMMANDTYPE typeID, IUICommandHandler** ppCommandHandler)
	{
		UIAddRibbonElement(nCmdID);
		if (typeID == UI_COMMANDTYPE_CONTEXT)
			CUpdateUIBase::UIEnable(nCmdID, false);
		*ppCommandHandler = this;
		return S_OK;
	}

	STDMETHODIMP OnDestroyUICommand(UINT32 nCmdID, UI_COMMANDTYPE, IUICommandHandler*)
	{
		UIRemoveRibbonElement(nCmdID);
		return S_OK;
	}

	// IUICommandHandler
	STDMETHODIMP Execute(UINT nCmdID,
		UI_EXECUTIONVERB verb, 
		const PROPERTYKEY* key,
		const PROPVARIANT* ppropvarValue,
		IUISimplePropertySet* pCommandExecutionProperties)
	{
		TFrame* pFrame =static_cast<TFrame*>(this);
		return pFrame->GetRibbonCtrl(nCmdID).DoExecute(nCmdID, verb, key, ppropvarValue, pCommandExecutionProperties);	
	}

	STDMETHODIMP UpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
								const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		TFrame* pFrame =static_cast<TFrame*>(this);
		return pFrame->GetRibbonCtrl(nCmdID).DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);	
	}

#ifdef _DEBUG
	// IUnknown methods (heavyweight)
	STDMETHODIMP_(ULONG) AddRef()
	{
		return InterlockedIncrement(&m_cRef);
	}

	STDMETHODIMP_(ULONG) Release()
	{
		LONG cRef = InterlockedDecrement(&m_cRef);
		if (cRef == 0) // NoOp for breakpoint
		{
			cRef = 0;
		}

		return cRef;
	}

	STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
	{
		if (ppv == NULL)
		{
			return E_POINTER;
		}
		else if ((iid == __uuidof(IUnknown)) ||
				 (iid == __uuidof(IUICommandHandler)) ||
				 (iid == __uuidof(IUIApplication)))
		{
			*ppv = this;
			AddRef();
			return S_OK;
		}
		else
		{
			return E_NOINTERFACE;
		}
	}

	LONG m_cRef;
#else
	// IUnknown methods (lightweight)
	STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
	{
		if ((iid == __uuidof(IUnknown)) ||
			(iid == __uuidof(IUICommandHandler)) ||
			(iid == __uuidof(IUIApplication)))
		{
			*ppv = this;
			return S_OK;
		}
		return E_NOINTERFACE;
	}
	ULONG STDMETHODCALLTYPE AddRef()
	{
		return 1;
	}
	ULONG STDMETHODCALLTYPE Release()
	{
		return 1;
	}
#endif

// CRibbonImpl ICtrl implementation
	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* /*pCommandExecutionProperties*/)
	{
		if (key != NULL)
		{
			if(k_(*key) != k_BooleanValue)
			{
				ATLTRACE(L"Control ID %d is not handled\n", nCmdID);
				return E_NOTIMPL;
			}
			BOOL bChecked = FALSE;
			ATLVERIFY(SUCCEEDED(PropVariantToBoolean(*ppropvarValue, &bChecked)));
			CUpdateUIBase::UISetCheck(nCmdID, bChecked);
		}

		ATLASSERT(verb == UI_EXECUTIONVERB_EXECUTE);
		verb;   // avoid level 4 warning

		static_cast<TFrame*>(this)->OnRibbonCommandExecute(nCmdID);
		
		return S_OK;
	}

	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
		const PROPVARIANT* /*ppropvarCurrentValue*/, PROPVARIANT* ppropvarNewValue)
	{
		TFrame* pFrame = static_cast<TFrame*>(this);
		HRESULT hr = E_NOTIMPL;
		switch (k_(key))
		{
		case k_LargeImage:
		case k_LargeHighContrastImage:
		case k_SmallImage:
		case k_SmallHighContrastImage:
			if (HBITMAP hbm = pFrame->OnRibbonQueryImage(nCmdID, key))
				hr = SetPropertyVal(key, GetImageCopy(hbm, UI_OWNERSHIP_TRANSFER), ppropvarNewValue);
			break;
		case k_Label:
		case k_Keytip:
		case k_TooltipTitle:
		case k_TooltipDescription:
		case k_LabelDescription:
			if (LPCWSTR sText = pFrame->OnRibbonQueryText(nCmdID, key))
				hr = SetPropertyVal(key, sText, ppropvarNewValue);
			break;
		case k_BooleanValue:
		case k_Enabled:
			hr = SetPropertyVal(key, pFrame->OnRibbonQueryState(nCmdID, key), ppropvarNewValue);
			break;
		case k_ContextAvailable:
			hr = SetPropertyVal(key, pFrame->OnRibbonQueryTabAvail(nCmdID), ppropvarNewValue);
			break;
		}
		return hr;
	}
	


// RecentItemsCtrlImpl
//
template <class TDocList = CRecentDocumentList>
class RecentItemsCtrlImpl : 
		public CBaseCtrlImpl<TFrame>,
		public CCallbackCollection<RecentItemsCtrlImpl<TDocList>, CCallbackProperty>,
		public TDocList
{
	typedef RecentItemsCtrlImpl<TDocList> thisClass;
	typedef CCallbackCollection<RecentItemsCtrlImpl<TDocList>, CCallbackProperty> baseClass;
public:
	typedef thisClass RecentItems;

	RecentItemsCtrlImpl(WORD wID) : baseClass(TDocList::m_nMaxEntries_Max),
		CBaseCtrlImpl<TFrame>(wID)
	{
	}

	// Implementation
	HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
	{
		ATLASSERT((INT)uItem < GetMaxEntries());

		LPCWSTR sPath = m_arrDocs[uItem].szDocName;
		HRESULT hr = E_NOTIMPL;
		switch (k_(key))
		{
		case k_Label:
			hr = SetPropertyVal(key, GetWndRibbon().OnRibbonQueryRecentItemName(sPath), value);
			break;
		case k_LabelDescription:
			hr = SetPropertyVal(key, sPath, value);
			break;
		default:
			ATLASSERT(FALSE);
			break;
		}

		return hr;
	}

	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT(nCmdID == GetID());
		ATLASSERT(ppropvarNewValue);

		HRESULT hr(E_NOTIMPL);
		switch (k_(key))
		{
		case k_RecentItems:
			if (SAFEARRAY* psa = SafeArrayCreateVector(VT_UNKNOWN, 0, m_arrDocs.GetSize()))
			{
				const int iLastIndex = m_arrDocs.GetSize() - 1;
				for (LONG i = 0; i <= iLastIndex; i++)
					SafeArrayPutElement(psa, &i, m_arrItems[iLastIndex - i]); // reverse order

				hr = SetPropertyVal(key, psa, ppropvarNewValue);
				SafeArrayDestroy(psa);
			}
			break;
		default:
			hr = baseClass::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
			if(E_NOTIMPL == hr)
				hr = CBaseCtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
			break;
		}

		return hr;
	}

	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* /*pCommandExecutionProperties*/)
	{
		ATLASSERT(nCmdID == GetID());
		nCmdID;   // avoid level 4 warning
		ATLASSERT(verb == UI_EXECUTIONVERB_EXECUTE);
		verb;   // avoid level 4 warning
		ATLASSERT((key) && (k_(*key) == k_SelectedItem));
		ATLASSERT(ppropvarValue);

		UINT32 uSel = 0xffff;
		HRESULT hr = UIPropertyToUInt32(*key, *ppropvarValue, &uSel);
		if SUCCEEDED(hr)
		{
			ATLASSERT(uSel < (UINT)GetMaxEntries());
			GetWndRibbon().CommandExecute(ID_FILE_MRU_FIRST + uSel);
		}

		return hr;
	}
};


///////////////////////////////////////////////////////////////////////////////
// Ribbon stand-alone control classes

// CRibbonFontCtrl
//
class CRibbonFontCtrl : public CBaseCtrlImpl<TFrame>
{
public:

	CharFormat m_cf;

	CRibbonFontCtrl(WORD wID) :
		CBaseCtrlImpl<TFrame>(wID)
	{
	}

// Implementation
	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* pCommandExecutionProperties)
	{
		ATLASSERT (nCmdID == GetID());
		nCmdID;   // avoid level 4 warning
		ATLASSERT ((key) && (k_(*key) == k_FontProperties));
		key;   // avoid level 4 warning

		HRESULT hr = E_INVALIDARG;
		switch (verb)
		{
			case UI_EXECUTIONVERB_PREVIEW:
			case UI_EXECUTIONVERB_EXECUTE:
				ATLASSERT(pCommandExecutionProperties);
				PROPVARIANT propvar;

				if (SUCCEEDED(hr = pCommandExecutionProperties->GetValue(UI_PKEY_FontProperties_ChangedProperties, &propvar)))
					m_cf << ATL::CComQIPtr<IPropertyStore>(propvar.punkVal);
				break;

			case UI_EXECUTIONVERB_CANCELPREVIEW:
				ATLASSERT(ppropvarValue);
				ATL::CComPtr<IPropertyStore> pStore;

				if (SUCCEEDED(hr = UIPropertyToInterface(UI_PKEY_FontProperties, *ppropvarValue, &pStore)))
					m_cf << pStore;
				break;
		}

		if (SUCCEEDED(hr))
			GetWndRibbon().OnRibbonFontCtrlExecute(GetID(), verb, &m_cf);
		else
			ATLASSERT(FALSE);

		return hr;
	}

	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		if ((k_(key) == k_FontProperties) && (GetWndRibbon().OnRibbonQueryFont(GetID(), m_cf)))
		{
			ATL::CComQIPtr<IPropertyStore> pStore(ppropvarCurrentValue->punkVal);
			m_cf >> pStore;
			return SetPropertyVal(key, pStore.p, ppropvarNewValue);
		}
		else
		{
			return CBaseCtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
		}
	}
};


typedef HRESULT ( TFrame::*PGETVALUEFUNC)(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value);
	typedef HRESULT ( TFrame::*PUPDATEVALUEFUNC)(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue);
	// Control map element
#pragma warning(push)
#pragma warning (disable : 4510 610) // missing default constructor
typedef struct 
{
	UINT uID;
	ICtrl& ctrl;
	PGETVALUEFUNC pGetFunc;
	PUPDATEVALUEFUNC pUpdateFunc;
} _ribbonCtrl;
#pragma warning (pop) // missing default constructor

template<class TCollection>
class CFrameCollection : TCollection
{
	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{

	}
};

// CRibbonColorCtrl
//
class CRibbonColorCtrl : public CButtonCtrl<TFrame>
{
public:
	CRibbonColorCtrl(WORD wID) : 
		CButtonCtrl<TFrame>(wID),
		m_colorType(UI_SWATCHCOLORTYPE_NOCOLOR), m_color(0x800080) /*MAGENTA*/
	{ }

	COLORREF m_color;
	UINT32 m_colorType; // value in UI_SWATCHCOLORTYPE
	Text m_sLabels[6]; // k_MoreColorsLabel to k_ThemeColorsCategoryLabel
	ATL::CSimpleArray<COLORREF> m_aColors[2];
	ATL::CSimpleArray<LPCWSTR> m_aTooltips[2];

	// Operations
	HRESULT SetColor(COLORREF color, bool bUpdate = false)
	{
		if (m_colorType != UI_SWATCHCOLORTYPE_RGB)
			SetColorType(UI_SWATCHCOLORTYPE_RGB, bUpdate);
		m_color = color;
		return bUpdate ? SetProperty(UI_PKEY_Color, color) : S_OK;
	}

	HRESULT SetColorType(UI_SWATCHCOLORTYPE type, bool bUpdate = false)
	{
		m_colorType = type;
		return bUpdate ? SetProperty(UI_PKEY_ColorType, type) : S_OK;
	}

	HRESULT SetColorLabel(REFPROPERTYKEY key, LPCWSTR sLabel, bool bUpdate = false)
	{
		ATLASSERT((k_(key) >= k_ThemeColorsCategoryLabel) && (k_(key) <= k_MoreColorsLabel));
		m_sLabels[k_(key) - k_ThemeColorsCategoryLabel] = sLabel;
		return bUpdate ? SetProperty(key, sLabel) : S_OK;
	}

	HRESULT SetColorArray(REFPROPERTYKEY key, COLORREF* pColor, bool bUpdate = false)
	{
		ATLASSERT((k_(key) == k_ThemeColors) || (k_(key) == k_StandardColors));

		const INT ic = k_(key) - k_ThemeColors;
		m_aColors[ic].RemoveAll();
		while (*pColor != 0x800080) /*MAGENTA*/
			m_aColors[ic].Add(*pColor++);

		if (bUpdate)
		{
			PROPVARIANT var;
			if SUCCEEDED(InitPropVariantFromUInt32Vector(m_aColors[ic].GetData(), m_aColors[ic].GetSize(), &var))
				return SetProperty(key, var);
			else
				return E_INVALIDARG;
		}
		else
		{
			return S_OK;
		}
	}

	HRESULT SetColorTooltips(REFPROPERTYKEY key, LPCWSTR* ppsTT, bool bUpdate = false)
	{
		ATLASSERT((k_(key) == k_ThemeColorsTooltips) || (k_(key) == k_StandardColorsTooltips));

		const INT ic = k_(key) - k_ThemeColorsTooltips;
		m_aTooltips[ic].RemoveAll();
		while (*ppsTT)
			m_aTooltips[ic].Add(*ppsTT++);

		if (bUpdate)
		{
			PROPVARIANT var;
			if SUCCEEDED(InitPropVariantFromStringVector(m_aTooltips[ic].GetData(), m_aTooltips[ic].GetSize(), &var))
				return SetProperty(key, var);
			else
				return E_INVALIDARG;
		}
		else
		{
			return S_OK;
		}
	}

	// Implementation
	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* pCommandExecutionProperties)
	{
		ATLASSERT (nCmdID == GetID());
		nCmdID;   // avoid level 4 warning
		ATLASSERT (key && (k_(*key) == k_ColorType));
		key;   // avoid level 4 warning
		ATLASSERT (ppropvarValue);

		HRESULT hr = PropVariantToUInt32(*ppropvarValue, &m_colorType);
		ATLASSERT(SUCCEEDED(hr));

		if (SUCCEEDED(hr) && (m_colorType == UI_SWATCHCOLORTYPE_RGB))
		{
			ATLASSERT(pCommandExecutionProperties);
			PROPVARIANT var;
			if SUCCEEDED(hr = pCommandExecutionProperties->GetValue(UI_PKEY_Color, &var))
				hr = PropVariantToUInt32(var, &m_color);
		}

		if SUCCEEDED(hr)
			GetWndRibbon().OnRibbonColorCtrlExecute(GetID(), verb, (UI_SWATCHCOLORTYPE)m_colorType/*uType*/, m_color);
		else
			ATLASSERT(FALSE); // something was wrong

		return hr;
	}

	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT (nCmdID == GetID());

		HRESULT hr = E_NOTIMPL;

		switch (k_(key))
		{
		case k_ColorType:
			hr = SetPropertyVal(key, m_colorType, ppropvarNewValue);
			break;
		case k_Color:
			if (m_color == 0x800080) /*MAGENTA*/
				m_color = GetWndRibbon().OnRibbonQueryColor(GetID());
			hr = SetPropertyVal(key, m_color, ppropvarNewValue);
			break;
		case k_ColorMode:
			break;
		case k_ThemeColorsCategoryLabel:
		case k_StandardColorsCategoryLabel:
		case k_RecentColorsCategoryLabel:
		case k_AutomaticColorLabel:
		case k_NoColorLabel:
		case k_MoreColorsLabel:
			{
				const UINT iLabel = k_(key) - k_ThemeColorsCategoryLabel;
				if (m_sLabels[iLabel].IsEmpty())
					if (LPCWSTR psLabel = GetWndRibbon().OnRibbonQueryColorLabel(GetID(), key))
						m_sLabels[iLabel] = psLabel;
				if (!m_sLabels[iLabel].IsEmpty())
					hr = SetPropertyVal(key, (LPCWSTR)m_sLabels[iLabel], ppropvarNewValue);
			}
			break;
		case k_ThemeColors:
		case k_StandardColors:
			{
				const INT ic = k_(key) - k_ThemeColors;
				if (!m_aColors[ic].GetSize())
					if (COLORREF* pColor = GetWndRibbon().OnRibbonQueryColorArray(GetID(), key))
						SetColorArray(key, pColor);
				if (INT iMax = m_aColors[ic].GetSize())
					hr = InitPropVariantFromUInt32Vector(m_aColors[ic].GetData(), iMax, ppropvarNewValue);
			}
			break;
		case k_ThemeColorsTooltips:
		case k_StandardColorsTooltips:
			{
				const INT ic = k_(key) - k_ThemeColorsTooltips;
				if (m_aTooltips[ic].GetSize() == 0)
					if (LPCWSTR* ppsTT = GetWndRibbon().OnRibbonQueryColorTooltips(GetID(), key))
						SetColorTooltips(key, ppsTT);
				if (INT iMax = m_aTooltips[ic].GetSize())
					hr = InitPropVariantFromStringVector(m_aTooltips[ic].GetData(), iMax, ppropvarNewValue);
			}
			break;
		default:
			hr = CButtonCtrl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
			break;
		}

		return hr;
	}
};

// CRibbonSpinnerCtrlT
//
template <typename V = LONG>
class CRibbonSpinnerCtrlT : public CBaseCtrlImpl<TFrame>
{
public:
	CRibbonSpinnerCtrlT(WORD wID) :
		CBaseCtrlImpl<TFrame>(wID)
	{
		m_Values[0] = m_Values[2] = m_Values[4] = 0;
		m_Values[1] = 100;
		m_Values[3] = 1;
	}

	V m_Values[5];
		// k_DecimalValue = 201, k_MaxValue = 203, k_MinValue, k_Increment, k_DecimalPlaces
		
	Text m_FormatString;
	Text m_RepresentativeString;

	// Operations
	HRESULT SetDecimalPlaces(V vPlaces, bool bUpdate = false)
	{
		return SetValue(UI_PKEY_DecimalPlaces, vPlaces, bUpdate);
	}

	HRESULT SetMin(V vMin, bool bUpdate = false)
	{
		return SetValue(UI_PKEY_MinValue, vMin, bUpdate);
	}

	HRESULT SetMax(V vMax, bool bUpdate = false)
	{
		return SetValue(UI_PKEY_MaxValue, vMax, bUpdate);
	}

	HRESULT SetVal(V vVal, bool bUpdate = false)
	{
		return SetValue(UI_PKEY_DecimalValue, vVal, bUpdate);
	}

	HRESULT SetIncrement(V vIncrement, bool bUpdate = false)
	{
		return SetValue(UI_PKEY_Increment, vIncrement, bUpdate);
	}

	HRESULT SetFormatString(LPCWSTR sFormat, bool bUpdate = false)
	{
		return SetText(UI_PKEY_FormatString, sFormat, bUpdate);
	}

	HRESULT SetRepresentativeString(LPCWSTR sRepresentative, bool bUpdate = false)
	{
		return SetText(UI_PKEY_RepresentativeString, sRepresentative, bUpdate);
	}

	// Implementation
	HRESULT SetText(REFPROPERTYKEY key, LPCWSTR sText, bool bUpdate = false)
	{
		switch (k_(key))
		{
		case k_FormatString:
			m_FormatString = sText;
			break;
		case k_RepresentativeString:
			m_RepresentativeString = sText;
			break;
		default:
			return CBaseCtrlImpl::SetText(key, sText, bUpdate);
		}

		return bUpdate ?
			GetWndRibbon().InvalidateProperty(GetID(), key) :
			S_OK;
	}

	HRESULT SetValue(REFPROPERTYKEY key, V val, bool bUpdate = false)
	{
		ATLASSERT((k_(key) <= k_DecimalPlaces) && (k_(key) >= k_DecimalValue));

		const INT iVal = k_(key) == k_DecimalValue ? 0 : k_(key) - k_StringValue;
		m_Values[iVal] = val;

		if (bUpdate)
		{
			if(k_(key) == k_DecimalValue)
			{
				DECIMAL decVal;
				InitDecimal(val, &decVal);
				return SetProperty(key, &decVal);
			}
			else
			{
				return GetWndRibbon().InvalidateProperty(GetID(), key);
			}
		}
		else
		{
			return S_OK;
		}
	}

	HRESULT QueryValue(REFPROPERTYKEY key, LONG* plVal)
	{
		return GetWndRibbon().OnRibbonQuerySpinnerValue(GetID(), key, plVal);
	}

	HRESULT QueryValue(REFPROPERTYKEY key, DOUBLE* pdVal)
	{
		return GetWndRibbon().OnRibbonQueryFloatSpinnerValue(GetID(), key, pdVal);
	}

	HRESULT OnGetValue(REFPROPERTYKEY key, PROPVARIANT* ppv)
	{
		ATLASSERT((k_(key) <= k_DecimalPlaces) && (k_(key) >= k_DecimalValue));

		const INT iVal = k_(key) == k_DecimalValue ? 0 : k_(key) - k_StringValue;

		QueryValue(key, m_Values + iVal);

		if (k_(key) == k_DecimalPlaces)
		{
			return SetPropertyVal(key, m_Values[iVal], ppv);
		}
		else
		{
			DECIMAL decVal;
			InitDecimal(m_Values[iVal], &decVal);
			return SetPropertyVal(key, &decVal, ppv);
		}
	}

	HRESULT OnGetText(REFPROPERTYKEY key, Text& sVal, PROPVARIANT* ppv)
	{
		if (LPCWSTR sNew = GetWndRibbon().OnRibbonQueryText(GetID(), key))
			sVal = sNew;
		return SetPropertyVal(key, (LPCWSTR)sVal, ppv);
	}

	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
							  const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
							  IUISimplePropertySet* /*pCommandExecutionProperties*/)
	{
		ATLASSERT (nCmdID == GetID());
		nCmdID;   // avoid level 4 warning
		ATLASSERT (key && (k_(*key) == k_DecimalValue));
		key;   // avoid level 4 warning
		ATLASSERT (verb == UI_EXECUTIONVERB_EXECUTE);
		verb;   // avoid level 4 warning

		DECIMAL decVal;

		HRESULT hr = UIPropertyToDecimal(UI_PKEY_DecimalValue, *ppropvarValue, &decVal);
		hr = InitVal(m_Values[0], &decVal);

		GetWndRibbon().OnRibbonSpinnerCtrlExecute(GetID(), &m_Values[0]);

		return hr;
	}

	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
									 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
	{
		ATLASSERT (nCmdID == GetID());

		HRESULT hr = E_NOTIMPL;
		switch (k_(key))
		{
		case k_DecimalPlaces:
		case k_DecimalValue:
		case k_Increment:
		case k_MaxValue:
		case k_MinValue:
			hr = OnGetValue(key, ppropvarNewValue);
			break;
		case k_FormatString:
			if (m_FormatString.IsEmpty())
				return OnGetText(key, m_FormatString, ppropvarNewValue);
			break;
		case k_RepresentativeString:
			if (m_RepresentativeString.IsEmpty())
				return OnGetText(key, m_RepresentativeString, ppropvarNewValue);
			break;
		default:
			hr = CBaseCtrlImpl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
			break;
		}

		return hr;
	}

	// decimal conversion helpers
	static HRESULT InitDecimal(LONG& val, DECIMAL* pDecimal)
	{
		return ::VarDecFromI4(val, pDecimal);
	}

	static HRESULT InitDecimal(DOUBLE& val, DECIMAL* pDecimal)
	{
		return ::VarDecFromR8(val, pDecimal);
	}

	static HRESULT InitVal(LONG& val, const DECIMAL* pDecimal)
	{
		return ::VarI4FromDec(pDecimal, &val);
	}

	static HRESULT InitVal(DOUBLE& val, const DECIMAL* pDecimal)
	{
		return ::VarR8FromDec(pDecimal, &val);
	}
};

// CRibbonSpinnerCtrl
typedef CRibbonSpinnerCtrlT<> CRibbonSpinnerCtrl;

// CRibbonFloatSpinnerCtrl
typedef CRibbonSpinnerCtrlT<DOUBLE> CRibbonFloatSpinnerCtrl;

// CRibbonImpl::CRibbonXXXCtrl specialized classes
	 //CRibbonComboCtrl
	class CRibbonComboCtrl : public CCollectionCtrlImpl<TFrame, CImageCollection<CRibbonComboCtrl> >
	{
	public:
		CRibbonComboCtrl(WORD wID, size_t items, size_t categories = 0) :
			CCollectionCtrlImpl<TFrame, CImageCollection<CRibbonComboCtrl>>(wID, items, categories)
		{ }

		// Operations
		HRESULT SetComboText(LPCWSTR sText)
		{
			WndRibbon& ribbon = GetWndRibbon();
			return ribbon.IsRibbonUI() ? 
				ribbon.SetProperty(static_cast<TCtrl*>(this)->GetID(), UI_PKEY_StringValue, sText) : 
				S_OK;
		}

		LPCWSTR GetComboText()
		{
			static WCHAR sCombo[RIBBONUI_MAX_TEXT] = { 0 };
			WndRibbon& ribbon = GetWndRibbon();
			PROPVARIANT var;
			if (ribbon.IsRibbonUI())
			{
				HRESULT hr = ribbon.GetIUIFrameworkPtr()->GetUICommandProperty(static_cast<TCtrl*>(this)->GetID(), UI_PKEY_StringValue, &var);
				hr = PropVariantToString(var, sCombo, RIBBONUI_MAX_TEXT);
				return sCombo;
			}
			return NULL;
		}
	};

	// CRibbonItemGalleryCtrl
	class CRibbonItemGalleryCtrl : public CCollectionCtrlImpl<TFrame, CImageCollection<CRibbonItemGalleryCtrl> >
	{
	public:
		CRibbonItemGalleryCtrl(WORD wID, size_t items, size_t categories = 0 ):
			CCollectionCtrlImpl<TFrame, CImageCollection<CRibbonItemGalleryCtrl> >(wID, items, categories)
		{ }
	};

	// CRibbonCommandGalleryCtrl
	class CRibbonCommandGalleryCtrl : 
		public CCollectionCtrlImpl<TFrame, CCommandCollectionImpl<CRibbonCommandGalleryCtrl> >
	{
	public:
		CRibbonCommandGalleryCtrl(WORD wID, size_t items, size_t categories = 0) :
			CollectionCtrlImpl<TFrame, CCommandCollectionImpl<CRibbonCommandGalleryCtrl> >(wID, items, categories)
		{ }
	};

	// CRibbonToolbarGalleryCtrl
	typedef CToolbarGalleryCtrlImpl<TFrame> CRibbonToolbarGalleryCtrl;

	// CRibbonSimpleComboCtrl
	typedef public CSimpleCollectionCtrlImpl<TFrame> CRibbonSimpleComboCtrl;

	// CRibbonSimpleGalleryCtrl
	typedef CSimpleCollectionCtrlImpl<TFrame> CRibbonSimpleGalleryCtrl;

	//CRibbonRecentItemsCtrl
	template <class TDocList = CRecentDocumentList>
	class CRibbonRecentItemsCtrl : public RecentItemsCtrlImpl<TDocList>
	{
	public:
		CRibbonRecentItemsCtrl(WORD wID) :
			RecentItemsCtrlImpl<TDocList>(wID)
		{ }
	};

	
	// CRibbonCommandCtrl
	typedef CButtonCtrl<TFrame> CRibbonCommandCtrl;

// Control classes access to TFrame instance (re-initialized in constructor)
	static TFrame* pWndRibbon;

};

template <class TFrame>
__declspec(selectany) TFrame* CRibbonImpl<TFrame>::pWndRibbon;

}; // namespace RibbonUI


///////////////////////////////////////////////////////////////////////////////
// RibbonUI Control map

// Control map macros
#define BEGIN_RIBBON_CONTROL_MAP(theClass) \
	_ribbonCtrl* GetRibbonCtrlStruct(UINT id) \
	{ \
		_ribbonCtrl _ctrls[] = \
		{

#define RIBBON_CONTROL(member) {member.GetID(), static_cast<RibbonUI::ICtrl&>(member), NULL, NULL},

//HRESULT OnGetValue(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value);
#define RIBBON_CONTROL_GET_FUNC(member, func) {member.GetID(), static_cast<RibbonUI::ICtrl&>(member), static_cast<PGETVALUEFUNC> (func), NULL},

//HRESULT OnUpdateValue(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue);
#define RIBBON_CONTROL_UPDATE_FUNC(member, func) {member.GetID(), static_cast<RibbonUI::ICtrl&>(member), NULL, static_cast<PUPDATEVALUEFUNC> (func)},

//HRESULT OnGetValue(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value);
//HRESULT OnUpdateValue(UINT nCmdID, REFPROPERTYKEY key, const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue);
#define RIBBON_CONTROL_UPDATE_GET_FUNCS(member, upfunc, getfunc) {member.GetID(), static_cast<RibbonUI::ICtrl&>(member), static_cast<PGETVALUEFUNC> (getfunc), static_cast<PUPDATEVALUEFUNC> (upfunc)},

#define END_RIBBON_CONTROL_MAP() \
			{0, *this} \
		}; \
		int i = 0; \
		for(; i < _countof(_ctrls) - 1; i++) \
			if (_ctrls[i].uID == id) \
				break; \
		return &(_ctrls[i]); \
	} \
	RibbonUI::ICtrl& GetRibbonCtrl(UINT id) \
	{ \
		return GetRibbonCtrlStruct(id)->ctrl; \
	}

// Control message map macros
#define RIBBON_GALLERY_CONTROL_HANDLER(id, func) \
	if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \
	{ \
		bHandled = TRUE; \
		lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (UINT)lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define RIBBON_COMBO_CONTROL_HANDLER(id, func) \
	RIBBON_GALLERY_CONTROL_HANDLER(id, func)	

#define RIBBON_FONT_CONTROL_HANDLER(id, func) \
	if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \
	{ \
		bHandled = TRUE; \
		lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (CHARFORMAT2*)lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define RIBBON_COLOR_CONTROL_HANDLER(id, func) \
	if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \
	{ \
		bHandled = TRUE; \
		lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (COLORREF)lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define RIBBON_SPINNER_CONTROL_HANDLER(id, func) \
	if(uMsg == WM_COMMAND && id == wParam) \
	{ \
		bHandled = TRUE; \
		lResult = func((WORD)wParam, (LONG)lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

#define RIBBON_FLOATSPINNER_CONTROL_HANDLER(id, func) \
	if(uMsg == WM_COMMAND && id == wParam) \
	{ \
		bHandled = TRUE; \
		lResult = func((WORD)wParam, (DOUBLE*)lParam, bHandled); \
		if(bHandled) \
			return TRUE; \
	}

// Handler prototypes
/*
	LRESULT OnRibbonGalleryCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled);
	LRESULT OnRibbonComboCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled);
	LRESULT OnRibbonFontCtrl(UI_EXECUTIONVERB verb, WORD wID, CHARFORMAT2* pcf, BOOL& bHandled);
	LRESULT OnRibbonColorCtrl(UI_EXECUTIONVERB verb, WORD wID, COLORREF color, BOOL& bHandled);
	LRESULT OnRibbonSpinnerCtrl(WORD wID, LONG lVal, BOOL& bHandled);
	LRESULT OnRibbonFloatSpinnerCtrl(WORD wID, DOUBLE* pdVal, BOOL& bHandled);
*/


///////////////////////////////////////////////////////////////////////////////
// Ribbon frame classes

// CRibbonFrameWindowImplBase
//
template <class TFrame, class TFrameImpl>
class ATL_NO_VTABLE CRibbonFrameWindowImplBase : public TFrameImpl, public RibbonUI::CRibbonImpl<TFrame>
{
	typedef TFrameImpl baseFrame;
	bool m_bUseCommandBarBitmaps;
	bool m_bWin7Fix;

public:
// Construction
	CRibbonFrameWindowImplBase(bool bUseCommandBarBitmaps = true) : 
			m_bUseCommandBarBitmaps(bUseCommandBarBitmaps), m_bWin7Fix(false)
	{
		__if_not_exists(TFrame::m_CmdBar)
		{
			m_bUseCommandBarBitmaps = false;
		}
	}

// Win7 Aero fix helpers
	void ResetFrame()
	{
		const MARGINS margins = { 0 };
		::DwmExtendFrameIntoClientArea(m_hWnd, &margins);
	}

	INT CalcWin7Fix()
	{
		ResetFrame();
		RECT rc = { 0 };
		::AdjustWindowRectEx(&rc, TFrame::GetWndStyle(0), GetMenu() != NULL, TFrame::GetWndExStyle(0));
		return -rc.top;
	}

	bool NeedWin7Fix()
	{
		BOOL bComp = FALSE;
		return m_bWin7Fix && RunTimeHelper::IsWin7() && SUCCEEDED(DwmIsCompositionEnabled(&bComp)) && bComp;
	}

// Operations
	bool UseCommandBarBitmaps(bool bUse)
	{
		__if_exists(TFrame::m_CmdBar)
		{
			return m_bUseCommandBarBitmaps = bUse;
		}
		__if_not_exists(TFrame::m_CmdBar)
		{
			bUse;   // avoid level 4 warning
			return false;
		}
	}

	bool ShowRibbonUI(bool bShow, INT32 imodes = UI_MAKEAPPMODE(0), LPCWSTR sResName = L"APPLICATION_RIBBON")
	{
		if (!RunTimeHelper::IsRibbonUIAvailable())
			return false;

		if (IsRibbonUI() == bShow)
			return bShow;

		bool bVisible = (IsWindowVisible() != FALSE);
		if(bVisible && !bShow)
			SetRedraw(FALSE);

		if (bShow && ::IsWindow(m_hWndToolBar))
		{
			::ShowWindow(m_hWndToolBar, SW_HIDE);
			UpdateLayout();
		}

		m_bWin7Fix = !bShow;

		HRESULT hr = bShow ? CreateRibbon(m_hWnd, this, sResName) : DestroyRibbon();

		m_bWin7Fix = SUCCEEDED(hr) && !bShow;

		if (SUCCEEDED(hr))
		{
			if(::IsWindow(m_hWndToolBar) && !bShow)
			{
				::ShowWindow(m_hWndToolBar, SW_SHOWNA);
				UpdateLayout(); 
			}
			else if (bShow)
			{
				PostMessage(WM_SIZE); 
				SetRibbonModes(imodes);
			}
		}

		if(bVisible && !bShow)
		{
			SetRedraw(TRUE);
			RedrawWindow(NULL, NULL, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
		}

		return SUCCEEDED(hr) ? bShow : !bShow;
	}

// Overrideables
	HBITMAP OnRibbonQueryImage(UINT nCmdID, REFPROPERTYKEY key)
	{
		if ((key == UI_PKEY_SmallImage) && m_bUseCommandBarBitmaps)
		{
			if (HBITMAP hbm = GetCommandBarBitmap(nCmdID))
				return (HBITMAP)::CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
		}

		return RibbonUI::CRibbonImpl<TFrame>::OnRibbonQueryImage(nCmdID, key);
	}

	BEGIN_MSG_MAP(CRibbonFrameWindowImplBase)
		if (!IsRibbonUI() && NeedWin7Fix())
		{
			MESSAGE_HANDLER(WM_SIZING, OnSizing)
			MESSAGE_HANDLER(WM_SIZE, OnSize)
			MESSAGE_HANDLER(WM_ACTIVATE, OnActivate)
			MESSAGE_HANDLER(WM_NCCALCSIZE, OnNCCalcSize)
		}
		CHAIN_MSG_MAP(CRibbonUpdateUI<TFrame>)
		CHAIN_MSG_MAP(baseFrame)
	END_MSG_MAP()

// Message handlers for Win7 Aero
	LRESULT OnSizing(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		switch (wParam)
		{		
		case WMSZ_TOP:
		case WMSZ_TOPLEFT:
		case WMSZ_TOPRIGHT:
			SetWindowPos(NULL, (LPRECT)lParam, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
			break;
		default:
			DefWindowProc();
			break;
		}

		return 1; // handled
	}

	LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if (wParam != SIZE_MINIMIZED)
			SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);

		bHandled = FALSE;
		return 1;
	}

	LRESULT OnActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if(wParam != WA_INACTIVE)
			SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);

		bHandled = FALSE;
		return 1;
	}

	LRESULT OnNCCalcSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		ATLASSERT(!IsRibbonUI() && NeedWin7Fix());

		LRESULT lRet = DefWindowProc();

		if(wParam)
		{
			LPNCCALCSIZE_PARAMS pParams = (LPNCCALCSIZE_PARAMS)lParam;
			pParams->rgrc[0].top = pParams->rgrc[1].top + CalcWin7Fix();
		}

		return lRet;
	}

// Overrides
	void UpdateLayout(BOOL bResizeBars = TRUE)
	{
		RECT rect = { 0 };
		GetClientRect(&rect);

		if (IsRibbonUI() && !IsRibbonHidden())
		{
			rect.top += GetRibbonHeight(); 
		}
		else if (!IsRibbonUI() && NeedWin7Fix())
		{
			ResetFrame();
		}

		// position bars and offset their dimensions
		UpdateBarsPosition(rect, bResizeBars);

		// resize client window
		if(m_hWndClient != NULL)
			::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top,
				rect.right - rect.left, rect.bottom - rect.top,
				SWP_NOZORDER | SWP_NOACTIVATE);
	}

	// Implementation
	HBITMAP GetCommandBarBitmap(UINT nCmdID)
	{
		__if_exists (TFrame::m_CmdBar)
		{
			ATLASSERT(RunTimeHelper::IsVista());
			TFrame* pFrame =static_cast<TFrame*>(this);
			int nIndex = pFrame->m_CmdBar.m_arrCommand.Find((WORD&)nCmdID);
			return (nIndex == -1) ? NULL : pFrame->m_CmdBar.m_arrVistaBitmap[nIndex];
		}
		__if_not_exists (TFrame::m_CmdBar)
		{
			nCmdID;   // avoid level 4 warning
			return NULL;
		}
	}
};

// CRibbonFrameWindowImpl
//
template <class TFrame, class TBase = ATL::CWindow, class TWinTraits = ATL::CFrameWinTraits>
class ATL_NO_VTABLE CRibbonFrameWindowImpl : public CRibbonFrameWindowImplBase<TFrame, CFrameWindowImpl<TFrame, TBase, TWinTraits>>
{ };

// CRibbonMDIFrameWindowImpl
//
template <class TFrame, class TBase = CMDIWindow, class TWinTraits = ATL::CFrameWinTraits>
class ATL_NO_VTABLE CRibbonMDIFrameWindowImpl : public CRibbonFrameWindowImplBase<TFrame, CMDIFrameWindowImpl<TFrame, TBase, TWinTraits>>
{ };


///////////////////////////////////////////////////////////////////////////////
// CRibbonPersist helper for RibbonUI persistency

class CRibbonPersist
{
public:
	CRibbonPersist(LPCWSTR sAppKey)
	{
		ATLASSERT(sAppKey && *sAppKey);
		m_Key.Create(HKEY_CURRENT_USER, sAppKey);
		ATLASSERT(m_Key.m_hKey);
	}

	CRegKeyEx m_Key;

	LONG Save(bool bRibbonUI, HGLOBAL hgSettings = NULL)
	{
		CRegKeyEx key;
		const DWORD dwUI = bRibbonUI;

		LONG lRet = key.Create(m_Key, L"Ribbon");
		if(lRet != ERROR_SUCCESS)
			return lRet;
		
		lRet = key.SetDWORDValue(L"UI", dwUI);
		if(lRet != ERROR_SUCCESS)
			return lRet;

		if (hgSettings != NULL)
		{
			LPBYTE pVal = (LPBYTE)::GlobalLock(hgSettings);
			if (pVal != NULL)
			{
				lRet = key.SetBinaryValue(L"Settings", pVal, (ULONG)::GlobalSize(hgSettings));
				::GlobalUnlock(hgSettings);
			}
			else
			{
				lRet = GetLastError();
			}
		}

		return lRet;
	}

	LONG Restore(bool& bRibbonUI, HGLOBAL& hgSettings)
	{
		ATLASSERT(hgSettings == NULL);

		CRegKeyEx key;

		LONG lRet = key.Open(m_Key, L"Ribbon");
		if(lRet != ERROR_SUCCESS)
			return lRet;
		
		DWORD dwUI = 0xffff;
		lRet = key.QueryDWORDValue(L"UI", dwUI);
		if(lRet == ERROR_SUCCESS)
			bRibbonUI = dwUI == 1;
		else
			return lRet;

		ULONG ulSize = 0;
		lRet = key.QueryBinaryValue(L"Settings", NULL, &ulSize);
		if (lRet == ERROR_SUCCESS)
		{
			ATLASSERT(ulSize != 0);
			
			hgSettings = ::GlobalAlloc(GHND, ulSize);
			if (hgSettings != NULL)
			{
				LPBYTE pData = (LPBYTE)::GlobalLock(hgSettings);
				if (pData != NULL)
				{
					lRet = key.QueryBinaryValue(L"Settings", pData, &ulSize);
				}
				else
				{
					lRet = GetLastError();
					::GlobalFree(hgSettings);
					hgSettings = NULL;
				}
			}
			else
			{
				lRet = GetLastError();
			}
		}
		return lRet;
	}

	LONG Delete()
	{
		return m_Key.DeleteSubKey(L"Ribbon");
	}
};

} // namespace WTL

#endif // __ATLRIBBON_H__
